Skip to content

orderBy+limit "requires an index" warning fires when an index exists but its compareOptions differ #2190

Closed

Description

@Jaime02
  • I've validated the bug against the latest version of DB packages

Describe the bug
When a query uses orderBy(...).limit(n) on a field that already has an index, but the query's compareOptions differ from the index's (for example nulls: "last" against an index built with the default nulls: "first"), DB logs:

[TanStack DB] [clients] orderBy with limit requires an index on "name" for efficient lazy loading. Falling back to loading all data. Consider creating an index on the collection with collection.createIndex((row) => row.name) or enable auto-indexing with autoIndex: 'eager' and a defaultIndexType.

Falling back is correct, because the index is sorted with different null ordering and matchesCompareOptions rejects it. The message is misleading, though: it says an index on name is required when one exists, and its suggested fix is the exact call that was already made. It doesn't say the index was skipped because of a compareOptions mismatch, or that createIndex accepts options.compareOptions.

To Reproduce

import { BasicIndex, createCollection, createLiveQueryCollection, localOnlyCollectionOptions } from "@tanstack/db";

type Client = { id: number; name: string };

const clients = createCollection(
  localOnlyCollectionOptions<Client>({
    id: "clients",
    getKey: (row) => row.id,
    initialData: [
      { id: 1, name: "Charlie" },
      { id: 2, name: "Alice" },
      { id: 3, name: "Bob" },
    ],
  }),
);

clients.createIndex((row) => row.name, { indexType: BasicIndex });

// No warning: matches the index's default compareOptions
const first = createLiveQueryCollection((q) =>
  q
    .from({ client: clients })
    .orderBy(({ client }) => client.name, { direction: "asc", nulls: "first" })
    .limit(2),
);
await first.preload();

// Warns that an index on "name" is required, although one exists
const last = createLiveQueryCollection((q) =>
  q
    .from({ client: clients })
    .orderBy(({ client }) => client.name, { direction: "asc", nulls: "last" })
    .limit(2),
);
await last.preload();
  1. bun add @tanstack/db@0.15.0
  2. Run the snippet above (bun repro.ts)
  3. The second query logs the warning above.

Creating the index with { options: { compareOptions: { direction: "asc", nulls: "last", stringSort: "locale" } } } makes the second query stop warning and the first one start warning, which shows the cause is the compareOptions mismatch and not a missing index.

Expected behavior
If an index on the field exists but findIndexForField rejects it because of compareOptions, the warning should say so and show which options to pass, e.g.:

orderBy with limit found an index on "name", but its compareOptions ({ nulls: "first", stringSort: "locale" }) don't match the query's ({ nulls: "last", stringSort: "locale" }). Create a matching index with collection.createIndex((row) => row.name, { options: { compareOptions: { nulls: "last", ... } } }).

Screenshots
N/A

Desktop (please complete the following information):

  • OS: Windows 11
  • Browser: also reproduces in Bun 1.4.0 (no browser needed)
  • Version: @tanstack/db 0.15.0

Smartphone (please complete the following information):

N/A

Additional context
The warning is emitted in query/compiler/order-by.ts right after findIndexForField(followRefCollection, followRefResult.path, compareOpts) returns undefined. findIndexForField checks both index.matchesField(fieldPath) and index.matchesCompareOptions(compareOpts), but the warning text only covers the first case. Checking whether any index passes matchesField would let the message tell the two cases apart.

Activity

  1. github-actions commented on Oct 18, 2026

    @github-actions
    Contributor

    🎉 The PR fixing this issue (#2196) has been released!

    Thank you for reporting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions