clockworklabs/SpacetimeDB · error
AddTable: table `{table_name}` not found in new module def
Error message
AddTable: table `{table_name}` not found in new module def What it means
Auto-migrate step AddTable: plan.new.find_table(table_name_key) returned None, so the table the plan wants to create is missing from the new module def. The migration plan's steps and the def they were derived from are inconsistent — an internal invariant break, not a user-facing validation error.
Source
Thrown at crates/engine/src/update.rs:289
let table_id = stdb.table_id_from_name_mut(tx, &table_name)?.unwrap();
if stdb.table_row_count_mut(tx, table_id).unwrap_or(0) > 0 {
anyhow::bail!(
"Cannot remove table `{table_name}`: table contains data. \
Clear the table's rows (e.g. via a reducer) before removing it from your schema."
);
}
log!(logger, "Dropping table `{table_name}`");
stdb.drop_table(tx, table_id)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::AddTable(table_name_key) => {
let (namespace, local) = table_name_key;
let table_name = joined(namespace, local);
let (owning_def, table_def) = plan
.new
.find_table(table_name_key)
.ok_or_else(|| anyhow::anyhow!("AddTable: table `{table_name}` not found in new module def"))?;
log!(logger, "Creating table `{table_name}`");
create_table_from_def_with_prefix(stdb, tx, owning_def, table_def, namespace)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::AddView(view_name_key) => {
let (namespace, local) = view_name_key;
let view_name = joined(namespace, local);
let (owning_def, view_def) = plan
.new
.find_view(view_name_key)
.ok_or_else(|| anyhow::anyhow!("AddView: view `{view_name}` not found in new module def"))?;
create_table_from_view_def_with_prefix(stdb, tx, owning_def, view_def, namespace)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveView(view_name_key) => {
let (namespace, local) = view_name_key;
let view_name = joined(namespace, local);
let view_id = stdb
.view_id_from_name_mut(tx, &view_name)?
.ok_or_else(|| anyhow::anyhow!("RemoveView: view `{view_name}` not found in database"))?;View on GitHub (pinned to 6dee26c6ef)
Solutions
- Regenerate the migration plan from the exact ModuleDef passed as plan.new
- Check the failing table key (namespace, local) matches the def's namespacing
- Report upstream with both defs if the plan came from the differ
Defensive patterns
Strategy: try-catch
Try / catch
match auto_migrate_database(&stdb, &mut tx, auth, &plan, &logger) {
Err(e) if e.to_string().contains("AddTable:") && e.to_string().contains("not found in new module def") => {
// plan/def desync: regenerate the plan from the same def, then retry once
let plan2 = MigratePlan::Auto(diff_defs(old_def, new_def)?);
auto_migrate_database(&stdb, &mut tx, auth, &plan2, &logger)
}
r => r,
} Prevention
- Never cache migration plans across def edits
- Compute steps and lookups from one def snapshot
- Add round-trip tests asserting every emitted step resolves in its def
When it happens
Trigger: A MigratePlan whose steps were computed against a different def than plan.new; namespace/prefix differences making find_table miss the key; a bug in the schema differ emitting an AddTable step for a table it dropped from the def.
Common situations: Hand-assembled or cached plans reused across def revisions; upstream diff bugs; submodule tables whose namespaced key changed between plan creation and apply.
Related errors
- Precheck: sequence `{sequence_name}` not found in new module
- AddView: view `{view_name}` not found in new module def
- 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
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/4b2799869f6ef62c.
Report an issue: GitHub.