clockworklabs/SpacetimeDB · error · anyhow::Error
Cannot define RLS rule on private table: {}. Please make tab
Error message
Cannot define RLS rule on private table: {}. Please make table public if you wish to restrict access using RLS. What it means
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.
Source
Thrown at crates/engine/src/sql/rls.rs:28
pub struct RowLevelExpr {
pub sql: ProjectName,
pub def: RowLevelSecuritySchema,
}
impl RowLevelExpr {
pub fn build_row_level_expr(
tx: &mut MutTxId,
auth_ctx: &AuthCtx,
rls: &RawRowLevelSecurityDefV9,
) -> anyhow::Result<Self> {
let (sql, _) = parse_and_type_sub(&rls.sql, &SchemaViewer::new(tx, auth_ctx), auth_ctx)?;
let table_id = sql.return_table_id().unwrap();
let schema = tx.schema_for_table(table_id)?;
match schema.table_access {
StAccess::Private => {
anyhow::bail!(
"Cannot define RLS rule on private table: {}. \
Please make table public if you wish to restrict access using RLS.",
schema.table_name
)
}
StAccess::Public => Ok(Self {
def: RowLevelSecuritySchema {
table_id,
sql: rls.sql.clone(),
},
sql,
}),
}
}
}
View on GitHub (pinned to 6dee26c6ef)
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
Example fix
// before
#[spacetimedb::table(private)]
pub struct Player { pub id: u64, pub owner: Identity }
// plus a row_level_security rule targeting Player
// after: RLS requires a public table
#[spacetimedb::table(public)]
pub struct Player { pub id: u64, pub owner: Identity } Defensive patterns
Strategy: validation
Validate before calling
#!/usr/bin/env bash # RLS rules must not target private tables if grep -rn -B3 'row_level_security' --include='*.rs' src | grep -q 'private'; then echo 'RLS defined on a private table will be rejected' >&2; exit 1 fi
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Index '${indexLabel}' on table '${tableLabel}' must define a
- Discovered cyclic dependency when resolving RLS rules for ta
- Failed to read value from the `{}` column of `{}` for table_
- query does not return plain table rows
- view definition cannot read from other views
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/062d04d50655dfbc.
Report an issue: GitHub.