clockworklabs/SpacetimeDB · critical
When replaying `st_column` update: `table_type` should not h
Error message
When replaying `st_column` update: `table_type` should not have changed, but previous schema has {:?} and new schema has {:?} What it means
While replaying an st_table update, the in-memory schema for that table already exists and its table_type (user vs system) differs from the value in the replayed st_table row. table_type is immutable for the lifetime of a table, so the commitlog disagrees with the state derived earlier in replay — corruption, version divergence, or a bug rewriting st_table rows.
Source
Thrown at crates/datastore/src/locking_tx_datastore/replay.rs:857
.expect("`st_table` should exist")
.any(|row_ref| row_ref.pointer() != new_st_table_entry.pointer())
}
/// Update the in-memory table structure for the table described by `row`,
/// in response to replay of a schema-altering migration.
fn reschema_table_for_st_table_update(&mut self, row: StTableRow) -> Result<()> {
// We only need to update if we've already constructed the in-memory table structure.
// If we haven't yet, then `self.get_table_and_blob_store_or_create` will see the correct schema
// when it eventually runs.
if let Ok((table, ..)) = self.get_table_and_blob_store_mut(row.table_id) {
table.with_mut_schema(|schema| -> Result<()> {
schema.table_access = row.table_access;
schema.primary_key = row.table_primary_key.map(|col_list| col_list.as_singleton().ok_or_else(|| anyhow::anyhow!("When replaying `st_column` update: `table_primary_key` should be a single column, but found {col_list:?}"))).transpose()?;
schema.table_name = row.table_name;
if row.table_type == schema.table_type {
Ok(())
} else {
Err(anyhow::anyhow!(
"When replaying `st_column` update: `table_type` should not have changed, but previous schema has {:?} and new schema has {:?}",
schema.table_type,
row.table_type,
).into())
}
})?;
}
Ok(())
}
/// Mark all `st_column` rows which refer to the same column as `st_column_row`
/// other than the one at `row_pointer` as outdated
/// by storing them in [`Self::replay_columns_to_ignore`].
///
/// Returns the ID of the table to which `st_column_row` belongs.
fn ignore_previous_versions_of_column(
&mut self,
st_column_row: &ProductValue,View on GitHub (pinned to 6dee26c6ef)
Solutions
- Open the database with the spacetimedb version that produced it
- Restore from a known-good snapshot
- Recreate the database if the data is disposable
- File a bug with the commitlog — table_type must never change for a given TableId
Defensive patterns
Strategy: fallback
Try / catch
Catch at open time, log the previous and replayed table_type from the message, and restore from a known-good snapshot; a table_type change is never legitimate, so treat the data dir as tainted.
Prevention
- Keep binaries version-locked to the data they open
- Validate backups by opening them after every upgrade
- Monitor for crashes during system-table rewrites and re-snapshot afterwards
When it happens
Trigger: Replaying a commitlog whose st_table row for an existing table carries a different table_type than the schema built earlier in the same replay; produced by log corruption, version skew, or faulty system-table rewrites.
Common situations: Opening a data directory with a mismatched spacetimedb build; crash during a system-table rewrite; databases migrated between versions that changed system-table semantics.
Related errors
- When replaying `st_column` update: `table_primary_key` shoul
- Error looking up name for truncated table {table_id:?}
- Delete for non-existent row when replaying transaction
- `CommittedState::replay_columns_to_ignore` should be empty a
- `CommittedState::replay_table_updated` should be empty at th
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/7e409261539b11cf.
Report an issue: GitHub.