{"record":{"id":"f8810b532a989093","repo":"clockworklabs/SpacetimeDB","slug":"committedstate-replay-columns-to-ignore-should","errorCode":null,"errorMessage":"`CommittedState::replay_columns_to_ignore` should be empty at the end of a commit, but found {} entries","messagePattern":"`CommittedState::replay_columns_to_ignore` should be empty at the end of a commit, but found (.+?) entries","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/datastore/src/locking_tx_datastore/replay.rs","lineNumber":1056,"sourceCode":"            return Ok(());\n        }\n\n        // Get the table for mutation.\n        let (table, blob_store, ..) = self.get_table_and_blob_store_mut(table_id)?;\n\n        // We do not need to consider a truncation of `st_table` itself,\n        // as if that happens, the database is bricked.\n\n        table.clear(blob_store);\n\n        Ok(())\n    }\n\n    fn replay_end_tx(&mut self) -> Result<()> {\n        self.next_tx_offset += 1;\n\n        if !self.replay_columns_to_ignore.is_empty() {\n            return Err(anyhow::anyhow!(\n                \"`CommittedState::replay_columns_to_ignore` should be empty at the end of a commit, but found {} entries\",\n                self.replay_columns_to_ignore.len(),\n            ).into());\n        }\n\n        if !self.replay_table_updated.is_empty() {\n            return Err(anyhow::anyhow!(\n                \"`CommittedState::replay_table_updated` should be empty at the end of a commit, but found {} entries\",\n                self.replay_table_updated.len(),\n            )\n            .into());\n        }\n\n        // Any dropped tables should be fully gone by the end of a transaction;\n        // if we see any reference to them in the future we should error, not ignore.\n        self.replay_table_dropped.clear();\n\n        Ok(())","sourceCodeStart":1038,"sourceCodeEnd":1074,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/datastore/src/locking_tx_datastore/replay.rs#L1038-L1074","documentation":"replay_end_tx is the commit boundary of CommittedState replay: every st_column row parked in replay_columns_to_ignore (superseded column versions awaiting their outdated markers) must have been consumed during the transaction. A non-empty set means a column-schema update started but never completed, so replay ends in an inconsistent state and fails instead of guessing.","triggerScenarios":"A commitlog transaction that interleaves st_column updates abnormally (e.g. add + drop column sequences that never emit the expected follow-up rows); a truncated log cutting a schema change in half; a bug in the replay bookkeeping that fails to drain the set.","commonSituations":"Crash during a schema migration commit; replaying logs written by a different spacetimedb version; fuzzed or hand-crafted commitlogs in testing.","solutions":["Restore from a snapshot predating the broken transaction","Replay with the version that wrote the schema change","Report the invariant violation to spacetimedb maintainers together with the commitlog — likely a replay bug, not user error"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"Catch at open, include the entry count from the message in the incident report, restore from a pre-migration snapshot, and file a spacetimedb bug with the commitlog — this is an internal invariant, not caller error.","preventionTips":["Replay-test migrations on a copy of production logs before upgrading","Snapshot immediately before schema migrations so a bad replay has a clean fallback","Track spacetimedb release notes for replay fixes and keep versions aligned"],"tags":["rust","spacetimedb","replay","internal-invariant","schema-change"],"backgroundTag":"internal-invariant-violation","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"}