{"record":{"id":"4e395415357d6a22","repo":"clockworklabs/SpacetimeDB","slug":"table-not-found-in-old-module-def","errorCode":null,"errorMessage":"table {} not found in old_module_def","messagePattern":"table (.+?) not found in old_module_def","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/engine/src/update.rs","lineNumber":173,"sourceCode":"    // Build a map from full-name (namespaced) -> (owning_def, table_def) covering root and all\n    // submodule tables. Submodule tables are stored in the DB with prefixed names like\n    // \"lib.library_procedure_timer\", but `ModuleDef::table()` only has the current level.\n    // `all_tables_with_prefix()` returns the owning submodule alongside each def, which is also\n    // needed so that `check_compatible` resolves column type refs against the correct\n    // (sub)module typespace.\n    let old_tables_by_name: std::collections::HashMap<String, _> = old_module_def\n        .all_tables_with_prefix()\n        .into_iter()\n        .map(|(prefix, owning_def, table_def)| (format!(\"{}{}\", prefix, &table_def.name[..]), (owning_def, table_def)))\n        .collect();\n\n    for table in existing_tables\n        .iter()\n        .filter(|table| table.table_type != StTableType::System && !table.is_view())\n    {\n        let (owning_def, old_def) = old_tables_by_name\n            .get(table.table_name.as_ref())\n            .ok_or_else(|| anyhow::anyhow!(\"table {} not found in old_module_def\", table.table_name))?;\n\n        table.check_compatible(owning_def, old_def)?;\n    }\n\n    match plan {\n        MigratePlan::Manual(plan) => manual_migrate_database(stdb, tx, plan, logger),\n        MigratePlan::Auto(plan) => auto_migrate_database(stdb, tx, auth_ctx, plan, logger),\n    }\n}\n\n/// Manually migrate a database.\nfn manual_migrate_database(\n    _stdb: &RelationalDB,\n    _tx: &mut MutTxId,\n    _plan: ManualMigratePlan,\n    _logger: &dyn UpdateLogger,\n) -> anyhow::Result<UpdateResult> {\n    unimplemented!(\"Manual database migrations are not yet implemented\")","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/engine/src/update.rs#L155-L191","documentation":"During update_database, every existing non-system, non-view table in the database must be found by full prefix-qualified name in the old module def used to build the migration plan. The error means the database contains a user table that the supplied old module def does not declare — the def and the actual database schema have drifted apart.","triggerScenarios":"Auto-migrating with an old module def that does not match the module actually published on the database; submodule table-name prefix mismatches (stored names look like lib.<submodule>.<table> while the def only has the local name); tables created outside the module (raw SQL DDL) that no module def covers.","commonSituations":"Version skew between a host's cached module def and the DB; republishing after submodule renames; mixing manual SQL DDL tables with module tables in one database.","solutions":["Republish the exact module currently initialized on the database so the old def matches reality before migrating","Check the failing table's full name for submodule-prefix issues (lib.<submodule>.<table>) on both sides","If the database is disposable, publish to a fresh database instead of migrating drifted state"],"exampleFix":"// before: plan built from a stale cached def\nlet plan = build_plan(cached_old_def, new_def);\nupdate_database(&stdb, &mut tx, auth, plan, &logger)?;\n\n// after: verify the def covers the DB before planning\nlet existing: Vec<String> = stdb.get_all_tables_mut(&mut tx)?\n    .iter().filter(|t| t.table_type != StTableType::System && !t.is_view())\n    .map(|t| t.table_name.to_string()).collect();\nfor name in &existing {\n    anyhow::ensure!(old_tables_by_name.contains_key(name), \"table {name} missing from old def\");\n}","handlingStrategy":"validation","validationCode":"// Verify every user table in the DB is declared by the old def before migrating\nlet declared: HashSet<String> = old_module_def\n    .all_tables_with_prefix()\n    .into_iter()\n    .map(|(prefix, _, t)| format!(\"{}{}\", prefix, &t.name[..]))\n    .collect();\nfor t in stdb.get_all_tables_mut(&mut tx)? {\n    if t.table_type != StTableType::System && !t.is_view() {\n        anyhow::ensure!(declared.contains(t.table_name.as_ref()),\n            \"table {} not covered by old def\", t.table_name);\n    }\n}","typeGuard":"fn table_is_module_managed(t: &TableSchema) -> bool {\n    t.table_type != StTableType::System && !t.is_view()\n}","tryCatchPattern":"match update_database(&stdb, &mut tx, auth, plan, &logger) {\n    Err(e) if e.to_string().contains(\"not found in old_module_def\") => {\n        // def/DB drift: refresh the old def or republish fresh; do not retry the same plan\n        anyhow::bail!(\"migration aborted; old def does not describe the database: {e:#}\");\n    }\n    r => r,\n}","preventionTips":["Build migration plans from the def of the module actually published","Avoid creating tables via raw SQL alongside module tables","In tests, publish fresh databases instead of migrating drifted fixtures"],"tags":["rust","spacetimedb","migration","schema","module-def"],"backgroundTag":"migration-schema-mismatch","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}