clockworklabs/SpacetimeDB · error
`table_id` must not be `TableId::SENTINEL` in `{row_level_se
Error message
`table_id` must not be `TableId::SENTINEL` in `{row_level_security_schema:#?}` What it means
create_row_level_security validates its input: a row-level-security policy must attach to a real table, but the RowLevelSecuritySchema arrived with table_id == TableId::SENTINEL, the placeholder used before the id allocator assigns a real TableId. The call is rejected before any row is written to st_row_level_security.
Source
Thrown at crates/datastore/src/locking_tx_datastore/mut_tx.rs:2551
.map(|mut iter| {
iter.next()
.map(|row| row.read_col(StConstraintFields::ConstraintId).unwrap())
})
}
/// Create a row level security policy.
///
/// Requires:
/// - `row_level_security_schema.table_id != TableId::SENTINEL`
/// - `row_level_security_schema.sql` must be unique.
///
/// Ensures:
///
/// - The row level security policy metadata is inserted into the system tables (and other data structures reflecting them).
/// - The returned `sql` is unique.
pub fn create_row_level_security(&mut self, row_level_security_schema: RowLevelSecuritySchema) -> Result<RawSql> {
if row_level_security_schema.table_id == TableId::SENTINEL {
return Err(anyhow::anyhow!(
"`table_id` must not be `TableId::SENTINEL` in `{row_level_security_schema:#?}`"
)
.into());
}
log::trace!(
"ROW LEVEL SECURITY CREATING for table: {}",
row_level_security_schema.table_id
);
// Insert the row into st_row_level_security
// NOTE: Because st_row_level_security has a unique index on sql, this will
// fail if already exists.
let row = StRowLevelSecurityRow {
table_id: row_level_security_schema.table_id,
sql: row_level_security_schema.sql,
};
View on GitHub (pinned to 9e0d92412f)
Solutions
- Create the table first, then copy the assigned id into the policy (row_level_security_schema.table_id = table_schema.table_id) before calling create_row_level_security
- If the schema came from deserialization, resolve table_id from the table name via the committed schema
- Add a debug_assert!/unit test over generated schemas asserting table_id != TableId::SENTINEL
Example fix
// before: schema built before the table exists, id never assigned
let rls = RowLevelSecuritySchema { table_id: TableId::SENTINEL, ..Default::default() };
tx.create_row_level_security(rls)?;
// after: create the table first and reuse its assigned id
let table = tx.create_table(table_schema)?;
let mut rls = RowLevelSecuritySchema { ..Default::default() };
rls.table_id = table.table_id;
tx.create_row_level_security(rls)?; Defensive patterns
Strategy: validation
Validate before calling
fn rls_schema_is_attachable(rls: &RowLevelSecuritySchema) -> bool {
rls.table_id != TableId::SENTINEL
}
// before creating the policy:
assert!(rls_schema_is_attachable(&rls), "RLS policy must reference a created table"); Prevention
- Always derive RLS schemas from a table schema returned by create_table, never from Default
- Write unit tests that walk generated module schemas and assert no system rows carry SENTINEL ids
- Order migrations: create table, then policies that reference it
- Treat TableId::SENTINEL anywhere outside table creation as a bug
When it happens
Trigger: Building a RowLevelSecuritySchema by hand or via Default/deserialization and calling create_row_level_security before the target table exists or before its assigned id was copied into the schema. TableSchema::from_module_def starts with TableId::SENTINEL until create_table assigns the real id.
Common situations: Code-first module tooling that defines RLS policies in the same pass as table creation; migrations reordered so the RLS step runs before table creation; module manifests deserialized from JSON/YAML that omit table_id.
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
- Failed to read value from the `{}` column of `{}` for table_
- Failed to generate table due to validation errors
- table schema should validate for query builder codegen
- schema entry '${accName}' must be a table or a submodule nam
- When replaying `st_column` update: `table_primary_key` shoul
AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20).
Data as JSON: /api/errors/471756175123749b.
Report an issue: GitHub.