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

  1. Make the table public (e.g. #[spacetimedb::table(public)] / drop the private attribute) -- RLS then governs per-row client access
  2. If the table must stay private, remove the RLS rule -- private already blocks all client reads/writes
  3. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/062d04d50655dfbc. Report an issue: GitHub.