clockworklabs/SpacetimeDB · error

`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`

Error message

`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`

What it means

MutTxId::create_sequence rejects a SequenceSchema whose table_id equals TableId::SENTINEL. A sequence must be attached to a concrete existing table column; the sentinel indicates the field was never populated, so creation is refused before any st_sequences row is written.

Source

Thrown at crates/datastore/src/locking_tx_datastore/mut_tx.rs:2147

    get_sequence_mut(seq_state, seq_id)?
        .gen_next_value()
        .ok_or_else(|| SequenceError::UnableToAllocate(seq_id).into())
}

impl MutTxId {
    /// Create a sequence.
    /// Requires:
    /// - `seq.sequence_id == SequenceId::SENTINEL`
    /// - `seq.table_id != TableId::SENTINEL`
    /// - `seq.sequence_name` must not be used for any other database entity.
    ///
    /// Ensures:
    /// - The sequence metadata is inserted into the system tables (and other data structures reflecting them).
    /// - The returned ID is unique and not `SequenceId::SENTINEL`.
    pub fn create_sequence(&mut self, seq: SequenceSchema) -> Result<SequenceId> {
        if seq.table_id == TableId::SENTINEL {
            return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{seq:#?}`").into());
        }

        let table_id = seq.table_id;
        let matching_system_table_schema = system_tables().iter().find(|s| s.table_id == table_id).cloned();

        if seq.sequence_id != SequenceId::SENTINEL && matching_system_table_schema.is_none() {
            return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`", seq).into());
        }

        let sequence_id = seq.sequence_id;

        log::trace!(
            "SEQUENCE CREATING: {} for table: {} and col: {}",
            seq.sequence_name,
            table_id,
            seq.col_pos
        );

View on GitHub (pinned to 9e0d92412f)

Solutions

  1. Resolve the owning table's id (by name lookup in the transaction) and set seq.table_id before calling create_sequence
  2. Assert at construction time that table_id differs from TableId::SENTINEL to fail closer to the bug

Example fix

// before
let seq = SequenceSchema { table_id: TableId::SENTINEL, .. };
let sequence_id = tx.create_sequence(seq)?;

// after
let table_id = tx
    .table_id_from_name("t")
    .ok_or_else(|| anyhow::anyhow!("table t does not exist"))?
    .into();
let seq = SequenceSchema { table_id, .. };
let sequence_id = tx.create_sequence(seq)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// Resolve the owning table before building the sequence schema
let table_id = tx
    .table_id_from_name("t")
    .ok_or_else(|| anyhow::anyhow!("table t does not exist"))?
    .into();
let seq = SequenceSchema { table_id, .. };

Type guard

fn sequence_schema_has_table(seq: &SequenceSchema) -> bool {
    seq.table_id != TableId::SENTINEL
}

Try / catch

match tx.create_sequence(seq) {
    Err(e) if e.to_string().contains("table_id` must not be `TableId::SENTINEL``") => {
        // Set the owning table id and retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling create_sequence with a SequenceSchema built with the default/sentinel table_id - e.g. constructing the schema programmatically without resolving the owning table.

Common situations: Schema tooling and migrations that build SequenceSchema values from partial metadata; tests copying template schemas.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20). Data as JSON: /api/errors/2d47e35678f82d9b. Report an issue: GitHub.