clockworklabs/SpacetimeDB · error
unexpected insertion error: {e}
Error message
unexpected insertion error: {e} What it means
Raised by the bindings' insert path when `datastore_insert_bsatn` returns an errno other than the two the bindings know how to map (`UNIQUE_ALREADY_EXISTS` and `AUTO_INC_OVERFLOW`). Mapped errnos become proper `TryInsertError::UniqueConstraintViolation` / `TryInsertError::AutoIncOverflow` values returned from `try_insert`; any other errno panics with this message. So seeing it means the host rejected the insert for an unexpected reason — most often module/database schema drift or version skew.
Source
Thrown at crates/bindings/src/table.rs:1371
buf.clear();
buf.serialize_into(&row).unwrap();
// Insert row into table.
// When table has an auto-incrementing column, we must re-decode the changed `buf`.
let res = sys::datastore_insert_bsatn(table_id, &mut buf).map(|gen_cols| {
// Let the caller handle any generated columns written back by `sys::datastore_insert_bsatn` to `buf`.
T::integrate_generated_columns(&mut row, gen_cols);
row
});
res.map_err(|e| {
let err = match e {
sys::Errno::UNIQUE_ALREADY_EXISTS => {
T::UniqueConstraintViolation::get().map(TryInsertError::UniqueConstraintViolation)
}
sys::Errno::AUTO_INC_OVERFLOW => T::AutoIncOverflow::get().map(TryInsertError::AutoIncOverflow),
_ => None,
};
err.unwrap_or_else(|| panic!("unexpected insertion error: {e}"))
})
}
/// Update a row of type `T` to `row` using the index identified by `index_id`.
#[track_caller]
fn update<T: Table>(index_id: IndexId, mut row: T::Row, mut buf: IterBuf) -> T::Row {
let table_id = T::table_id();
// Encode the row as bsatn into the buffer `buf`.
buf.clear();
buf.serialize_into(&row).unwrap();
// Insert row into table.
// When table has an auto-incrementing column, we must re-decode the changed `buf`.
let res = sys::datastore_update_bsatn(table_id, index_id, &mut buf).map(|gen_cols| {
// Let the caller handle any generated columns written back by `sys::datastore_update_bsatn` to `buf`.
T::integrate_generated_columns(&mut row, gen_cols);
row
});View on GitHub (pinned to 6dee26c6ef)
Solutions
- Republish the module so the Rust row type and the database schema agree: `spacetime publish <db>`.
- Match the `spacetimedb-bindings` version in Cargo.toml to your server/CLI version.
- Switch `insert` to `try_insert` and log/handle `UniqueConstraintViolation` and `AutoIncOverflow` — it makes the expected failure modes explicit, and if this panic still fires the residual errno is diagnostic.
- Capture the `{e}` errno in the panic output and report it upstream if versions and schema are confirmed aligned.
Example fix
// before
ctx.db.user().insert(User { id: 0, email: new_email.clone(), .. })?;
// after - expected constraint errors become values, not panics
match ctx.db.user().try_insert(User { id: 0, email: new_email.clone(), .. }) {
Ok(_) => {}
Err(TryInsertError::UniqueConstraintViolation(_)) => { /* handle duplicate */ }
Err(TryInsertError::AutoIncOverflow(_)) => { /* handle overflow */ }
} Defensive patterns
Strategy: validation
Validate before calling
// Before inserting, check the columns backed by unique indexes:
let exists = ctx.db.user().email().filter(&row.email).next().is_some();
if exists {
// decide: reject, upsert, or skip — instead of hitting the constraint path
return Err("email already registered".into());
}
ctx.db.user().insert(row)?; Try / catch
// Handle the mapped constraint errors; treat anything else as fatal.
match ctx.db.user().try_insert(row) {
Ok(inserted) => { /* inserted row with generated cols */ }
Err(TryInsertError::UniqueConstraintViolation(v)) => { /* duplicate */ }
Err(TryInsertError::AutoIncOverflow(v)) => { /* overflow */ }
} Prevention
- Use `try_insert` instead of `insert` so unique/auto-inc failures are values, not panics.
- Republish after every schema change before exercising insert paths.
- Add integration tests that insert every table's rows to catch schema/module drift in CI.
When it happens
Trigger: Calling `ctx.db.<table>().insert(row)` or `try_insert(row)` where the encoded row violates something other than a unique/auto-inc constraint: row layout not matching the table's current column set after an unpublish/republish, bindings crate older/newer than the host's ABI, or a host-internal storage error.
Common situations: Adding a column to the Rust table type and forgetting to republish before inserting; deploying a module built with an outdated `spacetimedb-bindings` against an upgraded server; inserting very large rows that trip a host limit.
Related errors
- unexpected error from `datastore_delete_by_index_scan_range_
- unexpected error from `datastore_index_scan_range_bsatn`: {e
- unexpected update error: {e}
- unable to decode args
- Unrecognised extra bytes in the {description}
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/82123099497d2e00.
Report an issue: GitHub.