{"record":{"id":"4c702d2d9ab5da29","repo":"clockworklabs/SpacetimeDB","slug":"addrowlevelsecurity-rls-sql-rls-not-found-in","errorCode":null,"errorMessage":"AddRowLevelSecurity: RLS `{sql_rls}` not found in new module def","messagePattern":"AddRowLevelSecurity: RLS `(.+?)` not found in new module def","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/engine/src/update.rs","lineNumber":602,"sourceCode":"            spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangePrimaryKey(table_name_key) => {\n                let (namespace, local) = table_name_key;\n                let table_name = joined(namespace, local);\n                let (_owning_def, table_def) = plan.new.find_table(table_name_key).ok_or_else(|| {\n                    anyhow::anyhow!(\"ChangePrimaryKey: table `{table_name}` not found in new module def\")\n                })?;\n                log!(logger, \"Changing primary key for table `{table_name}`\");\n                stdb.alter_table_primary_key(tx, &table_name, table_def.primary_key)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddSchedule(_) => {\n                anyhow::bail!(\"Adding schedules is not yet implemented\");\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveSchedule(_) => {\n                anyhow::bail!(\"Removing schedules is not yet implemented\");\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddRowLevelSecurity(sql_rls) => {\n                log!(logger, \"Adding row-level security `{sql_rls}`\");\n                let rls = plan.new.lookup::<RawRowLevelSecurityDefV9>(sql_rls).ok_or_else(|| {\n                    anyhow::anyhow!(\"AddRowLevelSecurity: RLS `{sql_rls}` not found in new module def\")\n                })?;\n                let rls = RowLevelExpr::build_row_level_expr(tx, &auth_ctx, rls)?;\n\n                stdb.create_row_level_security(tx, rls.def)?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveRowLevelSecurity(sql_rls) => {\n                log!(logger, \"Removing row-level security `{sql_rls}`\");\n                stdb.drop_row_level_security(tx, sql_rls.clone())?;\n            }\n            spacetimedb_schema::auto_migrate::AutoMigrateStep::AddColumns(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!(\"AddColumns: table `{table_name}` not found in new module def\"))?;\n                let table_id = stdb.table_id_from_name_mut(tx, &table_name).unwrap().unwrap();\n                let column_schemas = column_schemas_from_defs(owning_def, &table_def.columns, table_id);","sourceCodeStart":584,"sourceCodeEnd":620,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/engine/src/update.rs#L584-L620","documentation":"Adding a row-level-security policy: the step carries the RLS SQL text itself as its key, and plan.new.lookup::<RawRowLevelSecurityDefV9>(sql_rls) returns None - the new def set has no RLS definition registered under exactly that SQL string. Any drift in the SQL text (formatting, quoting, normalization) or absence of the RLS in the applied defs makes the lookup miss. The publish aborts.","triggerScenarios":"Editing the RLS SQL text (even whitespace or quote style) while other schema changes share the same publish; the RLS present in the planner's input defs but absent or textually different in the def set actually applied; macro serialization differences across SDK versions.","commonSituations":"Iterating on RLS expressions alongside table changes; upgrading SDK versions that serialize or normalize RLS SQL differently; bundling RLS edits with renames.","solutions":["Add or change the RLS policy in a dedicated publish with no other schema edits.","Keep the SQL string byte-identical between publishes when you do not intend to change it.","Clean-rebuild to eliminate def/plan desync, then republish.","Align SDK/compiler versions between builds.","Dev: spacetime publish --delete-data <db>.","Report if a clean single-change publish still fails."],"exampleFix":"// before: RLS SQL edited together with other schema changes in one publish\n#[spacetimedb::table(row_level_security = \"SELECT * FROM t WHERE owner = :sender\")]\n// ... plus renames/column edits ...\n\n// after: two publishes\n// 1) schema changes only, RLS untouched\n// 2) update row_level_security alone","handlingStrategy":"validation","validationCode":"use spacetimedb_schema::auto_migrate::AutoMigrateStep;\nuse spacetimedb_schema::raw_def::v9::RawRowLevelSecurityDefV9;\nfor step in &plan.steps {\n    if let AutoMigrateStep::AddRowLevelSecurity(sql) = step {\n        if plan.new.lookup::<RawRowLevelSecurityDefV9>(sql).is_none() {\n            anyhow::bail!(\"RLS with SQL {sql:?} not registered in the new def set\");\n        }\n    }\n}","typeGuard":"fn rls_registered(plan: &AutoMigratePlan, sql: &str) -> bool {\n    plan.new.lookup::<RawRowLevelSecurityDefV9>(sql).is_some()\n}","tryCatchPattern":"match update_database(&stdb, tx, &plan, ...).await {\n    Err(e) if e.to_string().contains(\"AddRowLevelSecurity\")\n        && e.to_string().contains(\"not found in new module def\") => {\n        // RLS SQL key drift: republish the RLS change alone, SQL byte-identical elsewhere.\n    }\n    result => result?,\n}","preventionTips":["Add or edit RLS policies in a dedicated publish with no other schema changes.","Keep RLS SQL text byte-identical between publishes when not changing it.","Clean-rebuild before publishing so the planner and applied defs match exactly.","Align SDK versions to keep SQL serialization stable."],"tags":["spacetimedb","rust","schema-migration","row-level-security","rls","publish"],"backgroundTag":"schema-migration-plan-mismatch","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}