Firestore Composite Index Missing

`FAILED_PRECONDITION: The query requires an index` — multi-field where + orderBy demands a composite index.

You write a perfectly innocent-looking Firestore query:

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

Works fine in the local emulator. Deploys to prod and explodes:

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

Firestore’s strict rule: any query with multi-field where, or where + orderBy on different fields, needs a precomputed composite index. There’s no “auto-use index” like PostgreSQL — indexes are explicit schema you have to declare.

Common causes

Ordered by hit rate, highest first.

1. where + orderBy on different fields

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

Any where field + orderBy on a different field = composite index required.

How to spot it: where field ≠ orderBy field?

2. Multiple inequality operators (!=, <, <=, >, >=, NOT_IN)

// Index required
query(coll, where('status', '!=', 'archived'), orderBy('createdAt'))

Inequality filters alone require an index. Multiple inequality filters definitely do.

How to spot it: where uses !=, <, >, not-in, array-contains-any?

3. Multiple equality wheres + any orderBy

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

Two or more wheres (even all equality) + orderBy = composite index.

How to spot it: ≥ 2 where clauses?

4. First production run of this query

Emulator auto-creates virtual indexes on demand; production requires manual creation. Code merge → deploy → first user request → boom.

How to spot it: Emulator works, prod errors.

5. Index exists but still building

New indexes take minutes to hours (depending on data volume). During build, queries still throw FAILED_PRECONDITION.

How to spot it: Firebase Console → Firestore → Indexes → status is “Building” not “Enabled.”

6. Index built with wrong direction (asc vs desc)

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

Must be indexed as (authorId asc, createdAt desc). If you built desc + asc, it doesn’t match.

How to spot it: Open the error link — compare its suggested schema with existing indexes.

Shortest path to fix

Step 1: Click the URL in the error to auto-create

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

The URL prefills the schema. Sign in, click Create Index. Build takes minutes to hours (depends on data size).

Step 2: Sync to firestore.indexes.json

Creating only via Console can be lost (a later deploy can overwrite). Export to code:

firebase firestore:indexes > firestore.indexes.json

Or maintain manually:

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

Deploy:

firebase deploy --only firestore:indexes

Step 3: Wait for build to finish

firebase firestore:indexes --project my-prod
# Or check Console for status: Building / Enabled

Multi-million-doc collections can take hours. Until done, errors persist.

Step 4: Simplify the query if possible

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

// Simplified (single field, no composite index)
query(coll,
  where('authorId_status', '==', `${x}_published`), // composite key on write
  orderBy('createdAt', 'desc')
)

This “composite key on write” trick saves an index but requires you maintain authorId_status at write time.

Step 5: Catch it early in emulator

# Local emulator
firebase emulators:start --only firestore

# Run your app + e2e tests
# Whenever the emulator emits FAILED_PRECONDITION, add the suggested index

# Pre-deploy dry-run shows index diffs
firebase firestore:indexes

Step 6: Direction alignment

query(..., orderBy('a', 'desc'), orderBy('b', 'asc'))

Index must be (a DESCENDING, b ASCENDING). Auto-generated console links align; hand-written firestore.indexes.json is risky.

Prevention

  • Run every new query on the emulator first; add the required index to firestore.indexes.json proactively
  • Commit firestore.indexes.json to git; PR review surfaces index changes
  • CI step: firebase firestore:indexes --dry-run diffs prod vs. repo
  • Add indexes to large collections (millions of docs) during off-hours — building can affect reads
  • For stable query patterns, maintain a “query pattern → index” reference doc for new contributors
  • Don’t create indexes for every ad-hoc query — audit and remove unused ones periodically (each index has storage cost)
  • For complex queries, consider denormalizing (redundant fields) to reduce multi-where needs

Tags: #Firebase #Debug #Troubleshooting