clockworklabs/SpacetimeDB · error
{e}
Error message
{e} What it means
TableHandle::insert panics with the error returned by try_insert: a UniqueConstraintViolation when the row's unique-column or primary-key value is already present on a different row, or an AutoIncOverflow when an auto-increment column exhausted its type. Exact duplicates are no-ops (set semantics) - the panic is specifically a different row reusing a unique value.
Source
Thrown at crates/bindings/src/table.rs:65
///
/// The return value is the inserted row, with any auto-incrementing columns replaced with computed values.
/// The `insert` method always returns the inserted row,
/// even when the table contains no auto-incrementing columns.
///
/// (The returned row is a copy of the row in the database.
/// Modifying this copy does not directly modify the database.
/// See [`UniqueColumn::update`] if you want to update the row.)
///
/// May panic if inserting the row violates any constraints.
/// Callers which intend to handle constraint violation errors should instead use [`Self::try_insert`].
///
/// Inserting an exact duplicate of a row already present in the table is a no-op,
/// as SpacetimeDB is a set-semantic database.
/// This is true even for tables with unique constraints;
/// inserting an exact duplicate of an already-present row will not panic.
#[track_caller]
fn insert(&self, row: Self::Row) -> Self::Row {
self.try_insert(row).unwrap_or_else(|e| panic!("{e}"))
}
/// The error type for this table for unique constraint violations. Will either be
/// [`UniqueConstraintViolation`] if the table has any unique constraints, or [`Infallible`]
/// otherwise.
type UniqueConstraintViolation: MaybeError<UniqueConstraintViolation>;
/// The error type for this table for auto-increment overflows. Will either be
/// [`AutoIncOverflow`] if the table has any auto-incrementing columns, or [`Infallible`]
/// otherwise.
type AutoIncOverflow: MaybeError<AutoIncOverflow>;
/// Counterpart to [`Self::insert`] which allows handling failed insertions.
///
/// For tables with constraints, this method returns an `Err` when the insertion fails rather than panicking.
/// For tables without any constraints, [`Self::UniqueConstraintViolation`] and [`Self::AutoIncOverflow`]
/// will be [`std::convert::Infallible`], and this will be a more-verbose [`Self::insert`].
///View on GitHub (pinned to 6dee26c6ef)
Solutions
- Switch to try_insert and handle the Err(UniqueConstraintViolation) branch for expected duplicates.
- If upsert is intended, use the primary key column's insert_or_update / update APIs instead of insert.
- Pre-check existence via the unique column handle (ctx.db.my_table().id().find(value)) before inserting.
Example fix
// before: panics when a user with this email already exists
ctx.db.user().insert(User { email, name });
// after: handle the violation explicitly
if let Err(_e) = ctx.db.user().try_insert(User { email, name }) {
// unique constraint violated - update instead, or return an error to the client
}
// or, on a primary key column: ctx.db.user().id().insert_or_update(row); Defensive patterns
Strategy: try-catch
Validate before calling
let existing = ctx.db.user().email().find(&row.email);
if existing.is_some() {
// duplicate unique value - update instead of insert
} Try / catch
match ctx.db.user().try_insert(row) {
Ok(inserted) => inserted,
Err(err) => {
// UniqueConstraintViolation or AutoIncOverflow
Err(reject(format!("duplicate: {err}")))
}
} Prevention
- Default to try_insert in reducer code; reserve insert() for values you have already proven unique.
- Make seeding reducers idempotent (check-then-insert or try_insert).
- Use insert_or_update on the primary key when upsert semantics are intended.
When it happens
Trigger: Inserting a second row with the same #[unique]#[primary_key] value but different other fields; seeding logic running twice with fresh non-key fields; auto-inc i64 column that overflowed after ~9.2e18 inserts.
Common situations: Seed reducers re-run on every publish; two concurrent reducer calls inserting the same key (one transaction aborts with this panic); expecting upsert semantics from insert.
Related errors
- Failed to get table with name: {table_name}
- unique: unexpected error from datastore_delete_by_index_scan
- unexpected error from `datastore_index_scan_point_bsatn`: {e
- unexpected error from `datastore_delete_by_index_scan_point_
- table should exist in the database for AddConstraint
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/492845f05b6694d5.
Report an issue: GitHub.