clockworklabs/SpacetimeDB · error

`table_id` must not be `TableId::SENTINEL` in `{index_schema

Error message

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

What it means

MutTxId::create_index rejects an IndexSchema whose table_id equals TableId::SENTINEL. The sentinel is the unset marker; a real, datastore-allocated table id is required so the index can be attached to an existing table (the code also verifies the table exists via table_name). Passing the sentinel means the schema was built without resolving the target table first.

Source

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

        Ok(table_id)
    }

    /// Create an index.
    ///
    /// Requires:
    /// - `index.index_name` must not be used for any other database entity.
    /// - `index.index_id == IndexId::SENTINEL`
    /// - `index.table_id != TableId::SENTINEL`
    /// - `is_unique` must be `true` if and only if a unique constraint will exist on
    ///   `ColSet::from(&index.index_algorithm.columns())` after this transaction is committed.
    ///
    /// Ensures:
    /// - The index metadata is inserted into the system tables (and other data structures reflecting them).
    /// - The returned ID is unique and is not `IndexId::SENTINEL`.
    pub fn create_index(&mut self, mut index_schema: IndexSchema, is_unique: bool) -> Result<IndexId> {
        let table_id = index_schema.table_id;
        if table_id == TableId::SENTINEL {
            return Err(anyhow::anyhow!("`table_id` must not be `TableId::SENTINEL` in `{index_schema:#?}`").into());
        }

        log::trace!(
            "INDEX CREATING: {} for table: {} and algorithm: {:?}",
            index_schema.index_name,
            table_id,
            index_schema.index_algorithm
        );
        if self.table_name(table_id).is_none() {
            return Err(TableError::IdNotFoundState(table_id).into());
        }

        // Insert the index row into `st_indexes` and write back the `IndexId`.
        // NOTE: Because `st_indexes` has a unique index on `index_name`,
        // this will fail if the index already exists.
        let row: StIndexRow = index_schema.clone().into();
        let index_id = self
            .insert_via_serialize_bsatn(ST_INDEX_ID, &row)?

View on GitHub (pinned to 9e0d92412f)

Solutions

  1. Resolve the real table id first (look the table up by name in the same transaction) and set index_schema.table_id before calling create_index
  2. Add a debug assertion at schema construction sites that table_id != TableId::SENTINEL
  3. Check the error's schema dump to see which index was built incorrectly

Example fix

// before
let schema = IndexSchema { table_id: TableId::SENTINEL, .. };
let index_id = tx.create_index(schema, true)?;

// after
let table_id = tx
    .table_id_from_name("t")
    .ok_or_else(|| anyhow::anyhow!("table t does not exist"))?
    .into();
let schema = IndexSchema { table_id, .. };
let index_id = tx.create_index(schema, true)?;
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

fn index_schema_has_table(schema: &IndexSchema) -> bool {
    schema.table_id != TableId::SENTINEL
}

Try / catch

match tx.create_index(schema, is_unique) {
    Err(e) if e.to_string().contains("table_id` must not be `TableId::SENTINEL``") => {
        // Fill in the real table id and rebuild the schema
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling create_index with an IndexSchema constructed with the default/sentinel table_id - e.g. building the schema from partial metadata or copying a template without setting table_id.

Common situations: Programmatic schema construction in tools and tests; DDL translation layers that forget the table-id lookup step.

Related errors


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