clockworklabs/SpacetimeDB · error
`CommittedState::replay_table_updated` should be empty at th
Error message
`CommittedState::replay_table_updated` should be empty at the end of a commit, but found {} entries What it means
replay_end_tx requires replay_table_updated to be drained by the end of each committed transaction: entries are added when a new st_table row is seen during replay and removed when the corresponding old st_table row is deleted (visit_delete for ST_TABLE_ID). Non-empty means a table schema update ended without its predecessor being cleaned up, so replay fails rather than continue with ambiguous state.
Source
Thrown at crates/datastore/src/locking_tx_datastore/replay.rs:1063
// as if that happens, the database is bricked.
table.clear(blob_store);
Ok(())
}
fn replay_end_tx(&mut self) -> Result<()> {
self.next_tx_offset += 1;
if !self.replay_columns_to_ignore.is_empty() {
return Err(anyhow::anyhow!(
"`CommittedState::replay_columns_to_ignore` should be empty at the end of a commit, but found {} entries",
self.replay_columns_to_ignore.len(),
).into());
}
if !self.replay_table_updated.is_empty() {
return Err(anyhow::anyhow!(
"`CommittedState::replay_table_updated` should be empty at the end of a commit, but found {} entries",
self.replay_table_updated.len(),
)
.into());
}
// Any dropped tables should be fully gone by the end of a transaction;
// if we see any reference to them in the future we should error, not ignore.
self.replay_table_dropped.clear();
Ok(())
}
}
pub(super) fn build_sequence_state(datastore: &Locking, cs: &mut CommittedState) -> Result<()> {
let sequence_state = cs.build_sequence_state()?;
// Reset our sequence state so that they start in the right places.
*datastore.sequence_state.lock() = sequence_state;View on GitHub (pinned to 6dee26c6ef)
Solutions
- Restore from a snapshot taken before the affected transaction
- Replay with the spacetimedb version that wrote the schema change
- Report the invariant violation with the commitlog for diagnosis
Defensive patterns
Strategy: fallback
Try / catch
Catch at open, restore from a snapshot taken before the affected transaction, and report the invariant violation with the commitlog for root-causing.
Prevention
- Snapshot before schema migrations (renames, column adds)
- Keep all replaying nodes on the version that wrote the log
- Crash-mid-migration should trigger re-snapshot once recovered
When it happens
Trigger: A schema-change transaction whose st_table delete row is missing from the log (corruption or truncation); reordered system-table writes from a different version; a bug in the add/remove pairing during replay of renames or column additions.
Common situations: Crash mid table-rename or column-add commit; version skew between writer and replayer; fuzzed commitlogs.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- `CommittedState::replay_columns_to_ignore` should be empty a
- Error looking up name for truncated table {table_id:?}
- When replaying `st_column` update: `table_primary_key` shoul
- When replaying `st_column` update: `table_type` should not h
- Delete for non-existent row when replaying transaction
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/caddbbae58725ae8.
Report an issue: GitHub.