{"record":{"id":"dbe8f932383cbd35","repo":"clockworklabs/SpacetimeDB","slug":"indexid-index-id-does-not-exist","errorCode":null,"errorMessage":"IndexId `{index_id}` does not exist","messagePattern":"IndexId `(.+?)` does not exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/datastore/src/locking_tx_datastore/tx.rs","lineNumber":141,"sourceCode":"        self.committed_state_shared_lock.iter_by_col_eq(table_id, cols, value)\n    }\n}\n\nimpl TxId {\n    fn with_index<'a, R>(\n        &'a self,\n        table_id: TableId,\n        index_id: IndexId,\n        seek: impl FnOnce(TableAndIndex<'a>) -> R,\n    ) -> anyhow::Result<R> {\n        self.committed_state_shared_lock\n            .get_table(table_id)\n            .ok_or_else(|| anyhow::anyhow!(\"TableId `{table_id}` does not exist\"))\n            .and_then(|table| {\n                table\n                    .get_index_by_id_with_table(&self.committed_state_shared_lock.blob_store, index_id)\n                    .map(seek)\n                    .ok_or_else(|| anyhow::anyhow!(\"IndexId `{index_id}` does not exist\"))\n            })\n    }\n\n    /// Release this read-only transaction,\n    /// allowing new mutable transactions to start if this was the last read-only transaction.\n    ///\n    /// Returns:\n    /// - [`TxOffset`], the smallest transaction offset visible to this transaction.\n    /// - [`TxMetrics`], various measurements of the work performed by this transaction.\n    /// - `ReducerName`, the name of the reducer which ran within this transaction.\n    pub(super) fn release(self) -> (TxOffset, TxMetrics, Option<ReducerName>) {\n        // A read tx doesn't consume `next_tx_offset`, so subtract one to obtain\n        // the offset that was visible to the transaction.\n        //\n        // Note that technically the tx could have run against an empty database,\n        // in which case we'd wrongly return zero (a non-existent transaction).\n        // This doesn not happen in practice, however, as [RelationalDB::set_initialized]\n        // creates a transaction.","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/datastore/src/locking_tx_datastore/tx.rs#L123-L159","documentation":"TxId::with_index found the table but get_index_by_id_with_table returned None: no index with the given IndexId is attached to that table. The index was dropped, belongs to a different table, or has not been created yet (e.g. its creation is still a pending schema change).","triggerScenarios":"Index scans using an IndexId captured before the index was dropped or recreated; querying an index that exists only in a newer module version than the database runs; mixing up ids between tables; scanning an index whose creation transaction has not committed.","commonSituations":"Module updates dropping indexes while old plans cache IndexIds; replicas lagging behind the coordinator's schema; developer tools hardcoding index ids; race between schema change and readers.","solutions":["Look the index up by name via the current schema and use the resolved IndexId","Re-compile query plans after any module or schema update","Guard index usage with a startup schema check (iterate st_indexes or get_index_by_name)","Confirm the index-creating transaction actually committed"],"exampleFix":"// before: hardcoded IndexId from an earlier schema generation\nlet iter = tx.index_scan_range(table_id, hardcoded_index_id, &range)?;\n\n// after: resolve the IndexId by name at startup\nlet index_id = tx.index_id_by_name(table_id, index_name)?.expect(\"index exists\");\nlet iter = tx.index_scan_range(table_id, index_id, &range)?;","handlingStrategy":"validation","validationCode":"// Resolve the index by name at startup instead of trusting a stored IndexId\nfn resolve_index_id(tx: &TxId, table_id: TableId, index_name: &str) -> Option<IndexId> {\n    tx.committed_state_shared_lock\n        .get_table(table_id)?\n        .get_index_by_name(index_name)\n        .map(|(_, idx)| idx.index_id)\n}","typeGuard":null,"tryCatchPattern":"Catch the missing-index error, re-resolve the index by name from the current schema, recompile the plan, and retry once; if the index was dropped, fall back to a table scan or re-subscribe after schema refresh.","preventionTips":["Do not persist IndexIds — persist index names and resolve them at startup","Drop or rebuild cached plans whenever the module's index set changes","When adding an index, wait for the schema change to commit before routing queries to it"],"tags":["rust","spacetimedb","datastore","index-id","index-scan"],"backgroundTag":"index-not-found","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}