clockworklabs/SpacetimeDB · error
adding non-unique constraints is not supported (constraint o
Error message
adding non-unique constraints is not supported (constraint on table {table_id}) What it means
Only unique constraints are supported by the datastore today. create_constraint extracts the constrained columns via constraint.data.unique_columns(); if the schema describes any other kind of constraint, it is rejected up front, before any system-table mutation, with the table id in the message.
Source
Thrown at crates/datastore/src/locking_tx_datastore/mut_tx.rs:2340
Ok((table_id, schema))
}
/// Creates a constraint, making the corresponding indices unique.
///
/// This inserts constraint metadata AND converts the in-memory indices
/// from non-unique to unique. If the existing data contains duplicate
/// values in the constrained columns, an error is returned.
///
/// Pre-validation (before any system-table mutation):
/// - the constraint must be a unique one (the only kind supported today);
/// - the target table must already have at least one index on the constrained columns.
pub fn create_constraint(&mut self, constraint: ConstraintSchema) -> Result<ConstraintId> {
let table_id = constraint.table_id;
// (a) Only unique constraints are supported at the moment. Reject anything else
// up front, before writing to `st_constraint`.
let Some(cols) = constraint.data.unique_columns().cloned() else {
return Err(anyhow::anyhow!(
"adding non-unique constraints is not supported (constraint on table {table_id})"
)
.into());
};
let col_list: ColList = cols.into();
// (b) A unique constraint must be backed by at least one index on the same columns.
// Check on the committed table; the tx table's index set is kept in lockstep
// with the committed one by the datastore, so agreement is an invariant.
{
let (_, (commit_table, _, _)) = self.get_or_create_insert_table_mut(table_id)?;
if commit_table.get_indexes_by_cols(&col_list).is_empty() {
return Err(anyhow::anyhow!(
"unique constraint on table {table_id} column(s) {col_list:?} \
requires at least one backing index on those columns"
)
.into());
}View on GitHub (pinned to 9e0d92412f)
Solutions
- Model only unique constraints; enforce other kinds (checks, FKs) in module reducers or application logic
- Filter unsupported constraint kinds out of migration/DDL translation before they reach create_constraint
- Track the SpacetimeDB roadmap for non-unique constraint support
Defensive patterns
Strategy: validation
Validate before calling
// Filter to the supported constraint kind before calling the datastore
fn is_supported_constraint(schema: &ConstraintSchema) -> bool {
schema.data.unique_columns().is_some()
}
if !is_supported_constraint(&schema) {
return Err(anyhow::anyhow!("only unique constraints are supported; enforce other rules in reducers"));
} Type guard
fn is_unique_constraint(schema: &ConstraintSchema) -> bool {
schema.data.unique_columns().is_some()
} Try / catch
match tx.create_constraint(schema) {
Err(e) if e.to_string().contains("adding non-unique constraints is not supported") => {
// Drop the unsupported constraint or reimplement it as reducer-level validation
}
other => other,
} Prevention
- Map non-unique constraints to application/reducer logic during migration design
- Keep a compatibility checklist when porting SQL schemas to SpacetimeDB
When it happens
Trigger: Calling MutTxId::create_constraint with a ConstraintData variant that carries no unique columns - e.g. a check-style or foreign-key-style constraint schema.
Common situations: Tooling or SQL layers that translate general SQL constraints; forward-ported schema dumps; tests exercising unsupported constraint kinds.
Related errors
- Cannot add unique constraint on table {table_id} column(s) {
- AddConstraint: `{constraint_name}` not found in new module d
- `table_id` must not be `TableId::SENTINEL` in `{constraint:#
- unique constraint on table {table_id} column(s) {col_list:?}
- Deletion for non-existent table {table_id:?}... huh?
AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20).
Data as JSON: /api/errors/eec2222eb1257795.
Report an issue: GitHub.