{"record":{"id":"062d04d50655dfbc","repo":"clockworklabs/SpacetimeDB","slug":"cannot-define-rls-rule-on-private-table-pleas","errorCode":null,"errorMessage":"Cannot define RLS rule on private table: {}. Please make table public if you wish to restrict access using RLS.","messagePattern":"Cannot define RLS rule on private table: (.+?)\\. Please make table public if you wish to restrict access using RLS\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/engine/src/sql/rls.rs","lineNumber":28,"sourceCode":"\npub struct RowLevelExpr {\n    pub sql: ProjectName,\n    pub def: RowLevelSecuritySchema,\n}\n\nimpl RowLevelExpr {\n    pub fn build_row_level_expr(\n        tx: &mut MutTxId,\n        auth_ctx: &AuthCtx,\n        rls: &RawRowLevelSecurityDefV9,\n    ) -> anyhow::Result<Self> {\n        let (sql, _) = parse_and_type_sub(&rls.sql, &SchemaViewer::new(tx, auth_ctx), auth_ctx)?;\n        let table_id = sql.return_table_id().unwrap();\n        let schema = tx.schema_for_table(table_id)?;\n\n        match schema.table_access {\n            StAccess::Private => {\n                anyhow::bail!(\n                    \"Cannot define RLS rule on private table: {}. \\\n                        Please make table public if you wish to restrict access using RLS.\",\n                    schema.table_name\n                )\n            }\n            StAccess::Public => Ok(Self {\n                def: RowLevelSecuritySchema {\n                    table_id,\n                    sql: rls.sql.clone(),\n                },\n                sql,\n            }),\n        }\n    }\n}\n","sourceCodeStart":10,"sourceCodeEnd":44,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/engine/src/sql/rls.rs#L10-L44","documentation":"Row-level security restricts which rows authenticated clients can access, so it only applies to public tables -- a private table already denies all client access, making an RLS rule meaningless. build_row_level_expr parses the rule's SQL, resolves the target table, and rejects the definition when the table's access is StAccess::Private, naming the table in the message.","triggerScenarios":"Defining a row-level security rule (RLS macro/annotation in the module, or SQL row-level security definition) whose target table is declared private; or converting a public table to private while leaving its RLS rules in place.","commonSituations":"Adding RLS to 'further lock down' an already-private table; misunderstanding the access model -- RLS is the mechanism for exposing filtered public data, not an extra layer over private data.","solutions":["Make the table public (e.g. #[spacetimedb::table(public)] / drop the private attribute) -- RLS then governs per-row client access","If the table must stay private, remove the RLS rule -- private already blocks all client reads/writes","Prefer public + RLS over private whenever clients need filtered row access"],"exampleFix":"// before\n#[spacetimedb::table(private)]\npub struct Player { pub id: u64, pub owner: Identity }\n// plus a row_level_security rule targeting Player\n\n// after: RLS requires a public table\n#[spacetimedb::table(public)]\npub struct Player { pub id: u64, pub owner: Identity }","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\n# RLS rules must not target private tables\nif grep -rn -B3 'row_level_security' --include='*.rs' src | grep -q 'private'; then\n  echo 'RLS defined on a private table will be rejected' >&2; exit 1\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide the access model first: public+RLS for filtered client access, private for host-only data","When changing a table's access, audit its RLS rules in the same change","Remember private already denies every client read/write - RLS adds nothing there","Review RLS targets during schema diffs in code review"],"tags":["sql","rls","security","schema","table-access"],"backgroundTag":"schema-validation-failed","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"}