{"record":{"id":"130f3d2a2ed51b80","repo":"clockworklabs/SpacetimeDB","slug":"cannot-remove-table-table-name-table-contains","errorCode":null,"errorMessage":"Cannot remove table `{table_name}`: table contains data. Clear the table's rows (e.g. via a reducer) before removing it from your schema.","messagePattern":"Cannot remove table `(.+?)`: table contains data\\. Clear the table's rows \\(e\\.g\\. via a reducer\\) before removing it from your schema\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/engine/src/update.rs","lineNumber":274,"sourceCode":"                {\n                    anyhow::bail!(\"Precheck failed: added sequence {sequence_name} already has values in range\",);\n                }\n            }\n        }\n    }\n\n    log::info!(\"Running database update steps: {}\", stdb.database_identity());\n    let mut res = UpdateResult::Success;\n\n    for step in plan.steps {\n        match step {\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveTable(table_name_key) => {\n                let (namespace, local) = table_name_key;\n                let table_name = joined(namespace, local);\n                let table_id = stdb.table_id_from_name_mut(tx, &table_name)?.unwrap();\n\n                if stdb.table_row_count_mut(tx, table_id).unwrap_or(0) > 0 {\n                    anyhow::bail!(\n                        \"Cannot remove table `{table_name}`: table contains data. \\\n                         Clear the table's rows (e.g. via a reducer) before removing it from your schema.\"\n                    );\n                }\n\n                log!(logger, \"Dropping table `{table_name}`\");\n                stdb.drop_table(tx, table_id)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddTable(table_name_key) => {\n                let (namespace, local) = table_name_key;\n                let table_name = joined(namespace, local);\n                let (owning_def, table_def) = plan\n                    .new\n                    .find_table(table_name_key)\n                    .ok_or_else(|| anyhow::anyhow!(\"AddTable: table `{table_name}` not found in new module def\"))?;\n                log!(logger, \"Creating table `{table_name}`\");\n                create_table_from_def_with_prefix(stdb, tx, owning_def, table_def, namespace)?;\n            }","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/engine/src/update.rs#L256-L292","documentation":"Thrown by the auto-migrator when a new module version removes a table that still contains rows. SpacetimeDB refuses to drop non-empty tables during an automatic migration because that would silently destroy live data, so the whole update transaction is aborted with anyhow::bail.","triggerScenarios":"Publishing a module whose schema no longer declares a table that exists in the database, while stdb.table_row_count_mut(tx, table_id) returns > 0 for that table in the AutoMigrateStep::RemoveTable branch of the update plan.","commonSituations":"Renaming a table (old table dropped, new one added) on a database with data; cleaning up 'unused' tables that actually hold rows; iterating on schemas against a dev database that accumulated test data instead of being republished fresh.","solutions":["Clear the table's rows before publishing, e.g. spacetime sql <db> \"DELETE FROM <table>\", or call a cleanup reducer that deletes all rows.","If the data is disposable, delete and republish the database (spacetime delete <db> && spacetime publish <db>) so the table never exists at publish time.","Keep the table in the schema for one more publish cycle and remove it only after confirming it is empty.","Use the manual migration tooling (spacetime migrate) to author an explicit data-preserving migration instead of relying on auto-migrate."],"exampleFix":"-- before: publish removes table, rows still present -> error\nspacetime publish my-db\n\n-- after: clear rows first, then publish\nspacetime sql my-db \"DELETE FROM old_table\"\nspacetime publish my-db","handlingStrategy":"validation","validationCode":"-- before publishing a version that removes a table:\nspacetime sql my-db \"SELECT COUNT(*) AS n FROM old_table\"\n-- if n > 0, clear it first:\nspacetime sql my-db \"DELETE FROM old_table\"","typeGuard":null,"tryCatchPattern":"match publish(&db, &module).await {\n    Err(e) if e.to_string().contains(\"table contains data\") => {\n        // clear rows via reducer/SQL, then retry the publish once\n    }\n    other => other,\n}","preventionTips":["Treat table removal as a two-step deploy: clear rows first, publish the removal second.","Keep a cleanup reducer per disposable table so purging before schema changes is one call.","Run publishes against a disposable dev database first to surface destructive diffs."],"tags":["migration","schema","auto-migrate","database","data-loss-prevention"],"backgroundTag":"schema-migration-aborted","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"}