{"record":{"id":"15ce8fc02fc9f372","repo":"clockworklabs/SpacetimeDB","slug":"table-id-must-not-be-tableid-sentinel-in-co","errorCode":null,"errorMessage":"`table_id` must not be `TableId::SENTINEL` in `{constraint:#?}`","messagePattern":"`table_id` must not be `TableId::SENTINEL` in `(.+?)`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/datastore/src/locking_tx_datastore/mut_tx.rs","lineNumber":2256,"sourceCode":"    /// Requires:\n    /// - `constraint.constraint_name` must not be used for any other database entity.\n    /// - `constraint.constraint_id == ConstraintId::SENTINEL`.\n    /// - `constraint.table_id != TableId::SENTINEL`.\n    /// - The caller is responsible for ensuring that the backing indices on\n    ///   `ColSet::from(&constraint.data.unique_columns())` already have the correct\n    ///   uniqueness — this method does not touch the in-memory index uniqueness.\n    ///   Use [`Self::create_constraint`] if the indices need to be converted.\n    ///\n    /// Ensures:\n    /// - The constraint metadata is inserted into the system tables (and other data structures reflecting them).\n    /// - The returned ID is unique and is not `ConstraintId::SENTINEL`.\n    /// - The `bool` in the return value is `true` iff a new `st_constraint` row was\n    ///   inserted (and therefore a `PendingSchemaChange::ConstraintAdded` was pushed).\n    ///   It is `false` if an identical row already existed (idempotent re-insertion);\n    ///   in that case the schema and pending-changes list are untouched.\n    fn create_st_constraint(&mut self, mut constraint: ConstraintSchema) -> Result<(ConstraintId, bool)> {\n        if constraint.table_id == TableId::SENTINEL {\n            return Err(anyhow::anyhow!(\"`table_id` must not be `TableId::SENTINEL` in `{constraint:#?}`\").into());\n        }\n\n        let table_id = constraint.table_id;\n\n        log::trace!(\n            \"CONSTRAINT CREATING: {} for table: {} and data: {:?}\",\n            constraint.constraint_name,\n            table_id,\n            constraint.data\n        );\n\n        // Insert the constraint row into `st_constraint`.\n        // NOTE: Because `st_constraint` has a unique index on constraint_name,\n        // this will fail if the table already exists.\n        let constraint_row = StConstraintRow {\n            table_id,\n            constraint_id: constraint.constraint_id,\n            constraint_name: constraint.constraint_name.clone(),","sourceCodeStart":2238,"sourceCodeEnd":2274,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/9e0d92412ff2248f401a8ad12d535f2b5ac30912/crates/datastore/src/locking_tx_datastore/mut_tx.rs#L2238-L2274","documentation":"create_st_constraint (reached through MutTxId::create_constraint) requires the ConstraintSchema's table_id to differ from TableId::SENTINEL. A constraint must attach to a concrete existing table; the sentinel means the field was never populated, and creation is refused before the st_constraint row is inserted.","triggerScenarios":"Calling create_constraint with a ConstraintSchema built with the default/sentinel table_id - e.g. a DDL or migration layer that forgot to resolve the target table id.","commonSituations":"Programmatic schema construction; constraint schemas built from partial metadata in tools and tests.","solutions":["Resolve the target table's id in the same transaction and set constraint.table_id before calling create_constraint","Validate schema fields (non-sentinel table_id) at construction time to catch the bug earlier"],"exampleFix":"// before\nlet schema = ConstraintSchema { table_id: TableId::SENTINEL, .. };\nlet constraint_id = tx.create_constraint(schema)?;\n\n// after\nlet table_id = tx\n    .table_id_from_name(\"t\")\n    .ok_or_else(|| anyhow::anyhow!(\"table t does not exist\"))?\n    .into();\nlet schema = ConstraintSchema { table_id, .. };\nlet constraint_id = tx.create_constraint(schema)?;","handlingStrategy":"type-guard","validationCode":"// Resolve the target table before building the constraint schema\nlet table_id = tx\n    .table_id_from_name(\"t\")\n    .ok_or_else(|| anyhow::anyhow!(\"table t does not exist\"))?\n    .into();\nlet schema = ConstraintSchema { table_id, .. };","typeGuard":"fn constraint_schema_has_table(schema: &ConstraintSchema) -> bool {\n    schema.table_id != TableId::SENTINEL\n}","tryCatchPattern":"match tx.create_constraint(schema) {\n    Err(e) if e.to_string().contains(\"table_id` must not be `TableId::SENTINEL``\") => {\n        // Populate the real table id and retry\n    }\n    other => other,\n}","preventionTips":["Resolve table ids in the same transaction that creates the constraint","Validate constraint schemas at construction in migration tooling"],"tags":["spacetimedb","datastore","constraint","schema","sentinel"],"backgroundTag":"sentinel-id-misuse","analyzedSha":"9e0d92412ff2248f401a8ad12d535f2b5ac30912","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}