clockworklabs/SpacetimeDB · critical
table should exist in the database for AddConstraint
Error message
table should exist in the database for AddConstraint
What it means
Panic during SpacetimeDB automatic database migration. For an `AutoMigrateStep::AddConstraint` step, the migration plan says a constraint is being added to an existing table; the code looks the table up in the live database with `table_id_from_name_mut(tx, &table_full_name)` and `.expect("table should exist in the database for AddConstraint")`. Panic means the plan referenced a table name that the database does not contain — an invariant break between the computed migration plan and actual DB state.
Source
Thrown at crates/engine/src/update.rs:486
log!(
logger,
"Dropping constraint `{constraint_name}` on table `{table_full_name}`"
);
stdb.drop_constraint(tx, constraint_schema.constraint_id)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::AddConstraint(key) => {
let (namespace, constraint_name) = key;
let (owning_def, table_def) = plan
.new
.find_storing_table(namespace, constraint_name)
.ok_or_else(|| anyhow::anyhow!("AddConstraint: `{constraint_name}` not found in new module def"))?;
let table_full_name = joined(namespace, &table_def.name);
let constraint_def: &ConstraintDef = plan.new.lookup(key).ok_or_else(|| {
anyhow::anyhow!("AddConstraint: constraint `{constraint_name}` not found in new module def")
})?;
let table_id = stdb
.table_id_from_name_mut(tx, &table_full_name)?
.expect("table should exist in the database for AddConstraint");
let mut constraint_schema =
ConstraintSchema::from_module_def(owning_def, constraint_def, table_id, ConstraintId::SENTINEL);
// Apply namespace prefix for submodule constraints
constraint_schema.constraint_name = namespace.join_raw(&constraint_schema.constraint_name);
log!(
logger,
"Adding constraint `{constraint_name}` on table `{table_full_name}`"
);
stdb.create_constraint(tx, constraint_schema)?;
}
spacetimedb_schema::auto_migrate::AutoMigrateStep::AddSequence(key) => {
let (namespace, sequence_name) = key;
let (owning_def, table_def) = plan
.new
.find_storing_table(namespace, sequence_name)
.ok_or_else(|| anyhow::anyhow!("AddSequence: `{sequence_name}` not found in new module def"))?;
let table_full_name = joined(namespace, &table_def.name);View on GitHub (pinned to 6dee26c6ef)
Solutions
- Check whether the table was renamed or moved — if so, a rename/migration step must precede AddConstraint or you must publish as a breaking change (drop & recreate database).
- Verify the constraint and table names printed in the error (`Adding constraint \`X\` on table \`Y\``) against the actual database schema (`spacetime sql <db> 'SELECT * FROM st_table'` style introspection).
- If the database is disposable (dev), delete it and publish fresh instead of migrating.
- If names match visually, suspect namespace-join differences (submodule prefixing) — report upstream with the module and its previous version, since this is an internal plan/DB mismatch.
Example fix
# before: rename table AND add constraint in one publish -> plan adds constraint to a table name not in DB spacetime publish mydb --project mod2 # after: publish the rename first (auto-migrate handles it), then add the constraint in a second publish spacetime publish mydb --project rename-only spacetime publish mydb --project with-constraint
Defensive patterns
Strategy: validation
Validate before calling
// Before migrating, confirm every constrained table exists under its joined name
for (_, t) in plan.new.tables() {
if stdb.table_id_from_name_mut(tx, &joined(ns, &t.name))?.is_none() {
return Err(anyhow!("table `{}` missing; migration plan invalid", t.name));
}
} Try / catch
let r = std::panic::catch_unwind(AssertUnwindSafe(|| stdb.apply_auto_migrate(tx, plan)));
match r { Ok(res) => res?, Err(_) => return Err(MigrationFailed::db_state_mismatch) } Prevention
- Don't rename/move tables and add constraints in the same publish; split into two publishes.
- In dev, prefer `spacetime publish --delete-data` (fresh database) over risky auto-migrations.
- Inspect the DB's table names before migrating when the module's history is unclear.
When it happens
Trigger: Publishing an updated module whose auto-migration plan includes `AddConstraint` (e.g. adding a unique/index constraint) on a table whose fully-qualified name (namespace-qualified, submodules joined) is not present in the running database. This can happen when the table was created under a different name/path, the plan's namespace-joining logic produces a different string than what was stored, or the database state is stale/corrupted.
Common situations: Renaming tables or moving them between submodules while also adding constraints in the same publish; publishing against a database created by a much older SpacetimeDB version where table names were stored unprefixed; interrupted earlier migrations leaving partial state; mixing manual DDL with module-driven schema.
Related errors
- table {} not found in old_module_def
- Failed to generate table due to validation errors
- table schema should validate for query builder codegen
- expected ModuleDef to contain key, but it does not
- snapshot worker panicked
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/ce40713f56ec8f1f.
Report an issue: GitHub.