clockworklabs/SpacetimeDB · error

TableId `{table_id}` does not exist

Error message

TableId `{table_id}` does not exist

What it means

A read-only transaction (TxId) called table_scan for a TableId the committed state does not know. TableIds are numeric ids assigned at table creation; this one was never created, was dropped, or belongs to an older schema generation — committed_state_shared_lock has no entry for it, so the scan cannot even start.

Source

Thrown at crates/datastore/src/locking_tx_datastore/tx.rs:61

        = IndexScanRangeIter<'a>
    where
        Self: 'a;

    type PointIndexIter<'a>
        = IndexScanPointIter<'a>
    where
        Self: 'a;

    fn row_count(&self, table_id: TableId) -> u64 {
        self.committed_state_shared_lock
            .table_row_count(table_id)
            .unwrap_or_default()
    }

    fn table_scan<'a>(&'a self, table_id: TableId) -> anyhow::Result<Self::TableIter<'a>> {
        self.committed_state_shared_lock
            .table_scan(table_id)
            .ok_or_else(|| anyhow::anyhow!("TableId `{table_id}` does not exist"))
    }

    fn index_scan_range<'a>(
        &'a self,
        table_id: TableId,
        index_id: IndexId,
        range: &impl RangeBounds<AlgebraicValue>,
    ) -> anyhow::Result<Self::RangeIndexIter<'a>> {
        self.with_index(table_id, index_id, |i| i.seek_range_via_algebraic_value(range))?
            .map_err(|IndexCannotSeekRange| IndexError::IndexCannotSeekRange(index_id).into())
    }

    fn index_scan_point<'a>(
        &'a self,
        table_id: TableId,
        index_id: IndexId,
        point: &AlgebraicValue,
    ) -> anyhow::Result<Self::PointIndexIter<'a>> {

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Resolve the table name to its current TableId (schema_for_table or iterating st_table) before scanning
  2. Refresh query/subscription plans after schema changes instead of reusing compiled ids
  3. Handle the error by re-planning or re-subscribing rather than retrying the same id
  4. Check that the table actually exists in this database/replica

Example fix

// before: scan with a TableId cached before a module update
let iter = tx.table_scan(cached_table_id)?;

// after: resolve the id from the current schema by name first
let schema = committed_state.schema_for_table_by_name(table_name)?;
let iter = tx.table_scan(schema.table_id)?;
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the id from the live schema before scanning
fn resolve_table_id(tx: &TxId, name: &str) -> anyhow::Result<TableId> {
    tx.schema_for_table_by_name(name)
        .map(|s| s.table_id)
        .ok_or_else(|| anyhow::anyhow!("table {name} does not exist"))
}

Try / catch

On this error, treat the TableId as stale: re-resolve by name from the current schema; if the table was dropped, invalidate cached plans and re-subscribe instead of retrying the same id.

Prevention

When it happens

Trigger: Scanning with a TableId captured before a module update dropped or recreated the table; query or subscription plans compiled against a previous schema and replayed after it changed; passing TableId::SENTINEL or an id invented by the caller.

Common situations: Hot module updates where old clients hold compiled plans; internal code caching table ids across schema changes; tools reading table ids from stale metadata files.

Related errors


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