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

  1. Open the database with the spacetimedb version that produced it
  2. Restore from a known-good snapshot
  3. Recreate the database if the data is disposable
  4. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/7e409261539b11cf. Report an issue: GitHub.