Fix Firestore "The query requires an index" (FAILED_PRECONDITION)

Firestore throws FAILED_PRECONDITION because a multi-field where + orderBy needs a composite index. The fastest fix, the CLI workflow, and how to confirm it's built.

TL;DR: Firestore is telling you a query needs a composite index that doesn’t exist yet. The error message contains a pre-filled console link. Click it, sign in, click Create index, wait for the status to flip from Building to Enabled (minutes for small collections, up to hours for large ones), and the query works. Then copy the same index into firestore.indexes.json and commit it so the next deploy doesn’t lose it.

You write a perfectly innocent-looking query:

const q = query(
  collection(db, 'posts'),
  where('authorId', '==', uid),
  where('status', '==', 'published'),
  orderBy('createdAt', 'desc')
);

It works fine in the local emulator. You deploy to prod and it explodes:

FAILED_PRECONDITION: The query requires an index. You can create
it here: https://console.firebase.google.com/...

Firestore’s rule, unchanged as of June 2026: every query is served by an index. Single-field queries get auto-created indexes. But the moment you combine fields — two or more where clauses, or a where plus an orderBy on a different field — you need a composite index, and Firestore will not invent one at query time. Unlike PostgreSQL or MySQL, there is no planner that picks “close enough” indexes. The index is explicit schema you declare, and if the exact one isn’t there, the query fails fast.

Which bucket are you in?

SymptomMost likely causeJump to
where field differs from orderBy fieldNeeds a composite indexStep 1
Two or more where clauses + an orderByNeeds a composite indexStep 1
Works in emulator, fails only in prodIndex never created in prodStep 1
You already created it, still failingStill building, or wrong directionStep 3
Worked yesterday, broke after a deployDeploy overwrote the console-only indexStep 2

Common causes

Ordered by how often they bite, highest first.

1. where + orderBy on different fields

// Needs composite index: (authorId ASC, createdAt DESC)
query(coll, where('authorId', '==', x), orderBy('createdAt', 'desc'))

Any where field plus an orderBy on a different field requires a composite index. If where and orderBy are the same field, Firestore uses the auto-created single-field index and you’re fine.

2. Two or more equality wheres + any orderBy

// Needs composite index: (status ASC, authorId ASC, createdAt DESC)
query(coll,
  where('status', '==', 'published'),
  where('authorId', '==', x),
  orderBy('createdAt', 'desc')
)

Two or more where clauses — even if every one of them is == — combined with an orderBy need a composite index. Each field being individually indexed is not enough; the index has to cover the exact combination.

3. Range / inequality filters

// Index required
query(coll, where('price', '>=', 10), orderBy('createdAt'))

Any range or inequality operator (<, <=, >, >=, !=, not-in, array-contains-any) combined with an orderBy or another filter needs a composite index.

Worth knowing if you remember the old limitation: since the 2024 GA of range and inequality filters on multiple fields, Firestore does let you put inequalities on different fields in one query (for example where('price', '>', 10) and where('rating', '>', 4) together). The cap is at most 10 range/inequality fields per query, and these queries still require a composite index that lists those fields. Order the most selective field first for performance. See the official guide.

4. First production run of this query

The emulator builds virtual indexes on demand, so a query that has never run against prod can pass every local test and still fail the instant a real user triggers it. Code merge -> deploy -> first request -> FAILED_PRECONDITION.

How to spot it: emulator works, prod errors, and there’s no matching index in the console.

5. Index exists but is still building

A freshly created index throws FAILED_PRECONDITION until it finishes building. For a collection with millions of documents this can take 10-30 minutes; hundreds of millions can take hours. Reads and writes to the collection keep working during the build — only the query that needs this specific index fails.

How to spot it: Firebase Console -> Firestore Database -> Indexes -> the Composite tab shows the index as Building, not Enabled.

6. Index built with the wrong sort direction

query(coll, where('authorId', '==', x), orderBy('createdAt', 'desc'))

This needs (authorId ASCENDING, createdAt DESCENDING). If you hand-wrote the index with createdAt ASCENDING, it won’t match and the query still fails. The console link always generates the correct directions; hand-edited firestore.indexes.json is where this slips through.

How to spot it: open the error link and compare its pre-filled schema field-by-field against the index you already have.

Shortest path to fix

The full error looks like this:

FAILED_PRECONDITION: The query requires an index.
You can create it here:
https://console.firebase.google.com/project/your-app/firestore/indexes?create_composite=...

The create_composite link arrives with every field and direction pre-filled. Sign in, confirm it’s the right project, and click Create index. This is the single fastest fix and you should always do it first, because the generated schema is guaranteed to match the query.

If your stack swallows the error before you see it (some frameworks log only the message), check Cloud Logging or your server console for the full string — the link is in there.

Step 2: Save the index to firestore.indexes.json

An index created only in the console is not in your codebase, so a later firebase deploy --only firestore:indexes can delete it. Export what’s live into the file Firebase deploys from:

firebase firestore:indexes > firestore.indexes.json

Or maintain it by hand:

{
  "indexes": [
    {
      "collectionGroup": "posts",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "authorId", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    }
  ]
}

Then deploy it:

firebase deploy --only firestore:indexes

Commit firestore.indexes.json to git. From here on, your indexes are code that survives deploys and shows up in PR review.

Step 3: Wait for the build, and verify

Building is asynchronous. Until it’s done, the query keeps failing. Check status:

firebase firestore:indexes

Or in the console: Firestore Database -> Indexes -> Composite tab. When the row reads Enabled, the index is live. Re-run the exact failing query — it should now return rows instead of FAILED_PRECONDITION. That round-trip is the real confirmation it’s fixed; the Enabled status alone occasionally lags by a few seconds.

For collections in the millions of documents, expect 10-30 minutes; hundreds of millions can take hours. Reads and writes are unaffected during the build.

Step 4: Simplify the query if you can’t wait

If you’d rather not add another index, fold the two equality filters into one field you maintain at write time:

// Needs a composite index
query(coll,
  where('status', '==', 'published'),
  where('authorId', '==', x),
  orderBy('createdAt', 'desc')
)

// Single composite key written at write time -> only a single-field index needed
query(coll,
  where('authorId_status', '==', `${x}_published`),
  orderBy('createdAt', 'desc')
)

You still need an index for (authorId_status ASC, createdAt DESC), but this collapses the multi-equality combination, and it’s the standard trade-off when you have many such combinations and want to cap index count. The cost is that you must keep authorId_status consistent on every write.

Step 5: Catch it in the emulator before you deploy

# Start the local emulator
firebase emulators:start --only firestore

Run your app and end-to-end tests against it. Whenever the emulator logs FAILED_PRECONDITION, copy the suggested index into firestore.indexes.json then and there, before the code reaches prod. The emulator’s index hints match what prod will demand.

Prevention

  • Run every new query against the emulator first and add its required index to firestore.indexes.json before merging.
  • Commit firestore.indexes.json to git so index changes show up in PR review.
  • Add a CI step that runs firebase deploy --only firestore:indexes --dry-run (Firebase CLI 13.18.0+) to validate index changes without touching prod.
  • Build indexes on very large collections during off-peak hours; the build competes for resources.
  • For complex queries, consider denormalizing (a composite key field at write time) instead of stacking indexes.
  • Audit periodically and delete unused indexes — each one carries storage cost and slows writes slightly.

FAQ

How long does a composite index take to build? Seconds to a couple of minutes for small collections. Millions of documents: roughly 10-30 minutes. Hundreds of millions: hours. The collection stays readable and writable the whole time; only the query needing that index fails until it reads Enabled.

My index says Enabled but the query still fails. Why? Two usual culprits: the directions don’t match (you need authorId ASC, createdAt DESC but built createdAt ASC), or it’s a different index than the one the query wants. Open the error link again and compare its pre-filled schema field-by-field. Status can also lag a few seconds behind the real build — retry once.

It worked yesterday and broke after a deploy. What happened? You almost certainly created the index only in the console, and a firebase deploy --only firestore:indexes from a repo that didn’t contain it wiped it. Run firebase firestore:indexes > firestore.indexes.json, commit the file, and redeploy.

Why does it pass in the emulator but fail in production? The emulator builds virtual indexes on demand; production requires them to exist beforehand. A query that never ran against prod can sail through every local test and then fail on the first real request.

Can I have inequality filters on two different fields now? Yes. Since the 2024 GA of range and inequality filters on multiple fields, you can, up to 10 range/inequality fields per query. You still need a composite index covering them, and you should order the most selective field first.

Tags: #Firebase #Debug #Troubleshooting