clockworklabs/SpacetimeDB · error

`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`

Error message

`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`

What it means

MutTxId::create_sequence requires sequence_id to be SequenceId::SENTINEL for user tables so the datastore can allocate a fresh unique id. Pre-assigned ids are only tolerated when the sequence targets a system table (matched against system_tables()). Passing a concrete sequence_id for a user sequence violates the API contract and is rejected.

Source

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

    /// 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
        );

        // Insert the sequence row into st_sequences
        // NOTE: Because st_sequences has a unique index on sequence_name, this will
        // fail if the table already exists.
        let mut sequence_row = StSequenceRow {
            sequence_id,
            sequence_name: seq.sequence_name,
            table_id,

View on GitHub (pinned to 9e0d92412f)

Solutions

  1. Reset seq.sequence_id to SequenceId::SENTINEL and let create_sequence allocate a new id
  2. When replaying/restoring schema, drop the old ids for user objects and remap references after creation

Example fix

// before: replaying a captured schema with its old id
let seq = SequenceSchema { sequence_id: old_sequence_id, .. };
let new_id = tx.create_sequence(seq)?; // -> sequence_id must be SENTINEL

// after: let the datastore assign the id
let seq = SequenceSchema { sequence_id: SequenceId::SENTINEL, .. };
let new_id = tx.create_sequence(seq)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// When creating user sequences, always let the datastore assign the id
let seq = SequenceSchema { sequence_id: SequenceId::SENTINEL, .. };

Type guard

fn sequence_id_is_unset(seq: &SequenceSchema) -> bool {
    seq.sequence_id == SequenceId::SENTINEL || targets_system_table(seq.table_id)
}

Try / catch

match tx.create_sequence(seq) {
    Err(e) if e.to_string().contains("sequence_id` must be `SequenceId::SENTINEL``") => {
        // Reset sequence_id to the sentinel and retry; remap the returned id afterwards
    }
    other => other,
}

Prevention

When it happens

Trigger: Re-inserting a sequence with a previously allocated sequence_id on a user table; restoring schema objects by replaying old schema dumps; building SequenceSchema with a hardcoded id.

Common situations: Backup/restore tooling that replays captured schema rows; migrations that copy ids from another database; tests reusing fixture schemas.

Related errors


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