clockworklabs/SpacetimeDB · error
Index `{index_name}` not found in table `{table_full_name}`
Error message
Index `{index_name}` not found in table `{table_full_name}` What it means
Auto-migrate step RemoveIndex: the index was found in the old def, but scanning the database's table schema (table_schema.indexes) found no index whose index_name equals the namespace-joined stored name. The database does not have the index the plan intends to drop — drift between old def and live DB, or a stored-name/prefix mismatch.
Source
Thrown at crates/engine/src/update.rs:363
stdb.create_index(tx, index_schema, is_unique)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveIndex(key) => {
let (namespace, index_name) = key;
let (_owning_def, table_def) = plan
.old
.find_storing_table(namespace, index_name)
.ok_or_else(|| anyhow::anyhow!("RemoveIndex: `{index_name}` not found in old module def"))?;
let table_full_name = joined(namespace, &table_def.name);
let stored_name = namespace.join_raw(&index_name.clone().into());
let table_id = stdb.table_id_from_name_mut(tx, &table_full_name)?.unwrap();
let table_schema = stdb.schema_for_table_mut(tx, table_id)?;
let index_schema = table_schema
.indexes
.iter()
.find(|index| index.index_name == stored_name)
.ok_or_else(|| anyhow::anyhow!("Index `{index_name}` not found in table `{table_full_name}`"))?;
log!(logger, "Dropping index `{index_name}` on table `{table_full_name}`");
stdb.drop_index(tx, index_schema.index_id)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeTableAccessorName(table_name_key) => {
let (namespace, local) = table_name_key;
let table_name = joined(namespace, local);
let (_, new_table_def): (&ModuleDef, &spacetimedb_schema::def::TableDef) =
plan.new.find_table(table_name_key).ok_or_else(|| {
anyhow::anyhow!("ChangeTableAccessorName: `{table_name}` not found in new module def")
})?;
let table_id = stdb.table_id_from_name_mut(tx, &table_name)?.unwrap();
let new_alias = namespace.join(new_table_def.accessor_name.clone());
log!(
logger,
"Changing table accessor name for `{table_name}` to `{new_alias}`",View on GitHub (pinned to 6dee26c6ef)
Solutions
- Refresh the old def from the database's actual schema and regenerate the plan
- Compare the stored index_name (with namespace prefix and alias applied) against the plan's name to spot prefixing mismatches
- For disposable databases, republish fresh instead of reconciling drift
Example fix
// before: drop straight from the plan's key
let index_schema = table_schema.indexes.iter()
.find(|i| i.index_name == stored_name)
.ok_or_else(|| anyhow!("Index `{index_name}` not found in table `{table_full_name}`"))?;
// after: resolve by column set when the prefixed name is uncertain
let index_schema = table_schema.indexes.iter()
.find(|i| ColSet::from(i.algorithm.columns()) == index_cols)
.ok_or_else(|| anyhow!("Index `{index_name}` not found in table `{table_full_name}`"))?; Defensive patterns
Strategy: validation
Validate before calling
// Before RemoveIndex, confirm the index exists under its stored (prefixed) name
let stored_name = namespace.join_raw(&index_name.clone().into());
anyhow::ensure!(
stdb.schema_for_table_mut(&mut tx, table_id)?.indexes.iter()
.any(|i| i.index_name == stored_name),
"index {index_name} absent from {table_full_name}; regenerate the plan"
); Type guard
fn index_present(schema: &TableSchema, stored_name: &str) -> bool {
schema.indexes.iter().any(|i| i.index_name == stored_name)
} Try / catch
match auto_migrate_database(&stdb, &mut tx, auth, &plan, &logger) {
Err(e) if e.to_string().contains("not found in table") => {
// drift or prefix mismatch: compare stored index names before retrying
for i in &stdb.schema_for_table_mut(&mut tx, table_id)?.indexes {
log::warn!("stored index: {}", i.index_name);
}
anyhow::bail!("{e:#}");
}
r => r,
} Prevention
- Keep index aliases and submodule prefixes stable across publishes
- Do not drop module-managed indexes with raw SQL
- After partial migrations, refresh the old def from the DB before retrying
When it happens
Trigger: The index was already dropped in the database while the old def still lists it; stored name mismatch because submodule index names are stored prefixed (namespace.join_raw) and the lookup key differs; an earlier partially applied migration removed the index.
Common situations: Retrying migrations after partial application; alias/prefix changes for submodule indexes; manual index drops via SQL alongside module-managed indexes.
Related errors
- RemoveView: view `{view_name}` not found in database
- AddIndex: `{index_name}` not found in new module def
- AddIndex: index `{index_name}` not found in new module def
- RemoveIndex: `{index_name}` not found in old module def
- table {} not found in old_module_def
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/5cf09c4339a5340e.
Report an issue: GitHub.