{"record":{"id":"82123099497d2e00","repo":"clockworklabs/SpacetimeDB","slug":"unexpected-insertion-error-e","errorCode":null,"errorMessage":"unexpected insertion error: {e}","messagePattern":"unexpected insertion error: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings/src/table.rs","lineNumber":1371,"sourceCode":"    buf.clear();\n    buf.serialize_into(&row).unwrap();\n\n    // Insert row into table.\n    // When table has an auto-incrementing column, we must re-decode the changed `buf`.\n    let res = sys::datastore_insert_bsatn(table_id, &mut buf).map(|gen_cols| {\n        // Let the caller handle any generated columns written back by `sys::datastore_insert_bsatn` to `buf`.\n        T::integrate_generated_columns(&mut row, gen_cols);\n        row\n    });\n    res.map_err(|e| {\n        let err = match e {\n            sys::Errno::UNIQUE_ALREADY_EXISTS => {\n                T::UniqueConstraintViolation::get().map(TryInsertError::UniqueConstraintViolation)\n            }\n            sys::Errno::AUTO_INC_OVERFLOW => T::AutoIncOverflow::get().map(TryInsertError::AutoIncOverflow),\n            _ => None,\n        };\n        err.unwrap_or_else(|| panic!(\"unexpected insertion error: {e}\"))\n    })\n}\n\n/// Update a row of type `T` to `row` using the index identified by `index_id`.\n#[track_caller]\nfn update<T: Table>(index_id: IndexId, mut row: T::Row, mut buf: IterBuf) -> T::Row {\n    let table_id = T::table_id();\n    // Encode the row as bsatn into the buffer `buf`.\n    buf.clear();\n    buf.serialize_into(&row).unwrap();\n\n    // Insert row into table.\n    // When table has an auto-incrementing column, we must re-decode the changed `buf`.\n    let res = sys::datastore_update_bsatn(table_id, index_id, &mut buf).map(|gen_cols| {\n        // Let the caller handle any generated columns written back by `sys::datastore_update_bsatn` to `buf`.\n        T::integrate_generated_columns(&mut row, gen_cols);\n        row\n    });","sourceCodeStart":1353,"sourceCodeEnd":1389,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings/src/table.rs#L1353-L1389","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nctx.db.user().insert(User { id: 0, email: new_email.clone(), .. })?;\n\n// after - expected constraint errors become values, not panics\nmatch ctx.db.user().try_insert(User { id: 0, email: new_email.clone(), .. }) {\n    Ok(_) => {}\n    Err(TryInsertError::UniqueConstraintViolation(_)) => { /* handle duplicate */ }\n    Err(TryInsertError::AutoIncOverflow(_)) => { /* handle overflow */ }\n}","handlingStrategy":"validation","validationCode":"// Before inserting, check the columns backed by unique indexes:\nlet exists = ctx.db.user().email().filter(&row.email).next().is_some();\nif exists {\n    // decide: reject, upsert, or skip — instead of hitting the constraint path\n    return Err(\"email already registered\".into());\n}\nctx.db.user().insert(row)?;","typeGuard":null,"tryCatchPattern":"// Handle the mapped constraint errors; treat anything else as fatal.\nmatch ctx.db.user().try_insert(row) {\n    Ok(inserted) => { /* inserted row with generated cols */ }\n    Err(TryInsertError::UniqueConstraintViolation(v)) => { /* duplicate */ }\n    Err(TryInsertError::AutoIncOverflow(v)) => { /* overflow */ }\n}","preventionTips":["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."],"tags":["spacetimedb","insert","reducer-panic","schema-mismatch","constraint-violation"],"backgroundTag":"database-insert-failed","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}