clockworklabs/SpacetimeDB · error
Attempt to remove non-existent insert callback
Error message
Attempt to remove non-existent insert callback
What it means
The Rust client SDK registers on_insert callbacks by CallbackId in an internal map; `remove_on_insert` expects the id to be present and panics when `remove` returns None. These methods are pub(crate), so a user hitting this is experiencing SDK-internal double-removal or removal of an unregistered id — an SDK bug or version regression, not user callback misuse.
Source
Thrown at sdks/rust/src/callbacks.rs:157
pub(crate) fn register_on_delete(&mut self, callback_id: CallbackId, callback: RowCallback<M>) {
self.on_delete.insert(callback_id, callback);
}
pub(crate) fn register_on_update(&mut self, callback_id: CallbackId, callback: UpdateCallback<M>) {
self.on_update.insert(callback_id, callback);
}
pub(crate) fn remove_on_insert(&mut self, callback_id: CallbackId) {
// Ugly: `impl FnMut` is `must_use`.
// If we don't `.expect` this, no diagnostic,
// but we want to assert that we actually removed a callback,
// we just don't want to invoke it.
// So we have to `let _ =`.
let _ = self
.on_insert
.remove(&callback_id)
.expect("Attempt to remove non-existent insert callback");
}
pub(crate) fn remove_on_delete(&mut self, callback_id: CallbackId) {
// Ugly: `impl FnMut` is `must_use`.
// If we don't `.expect` this, no diagnostic,
// but we want to assert that we actually removed a callback,
// we just don't want to invoke it.
// So we have to `let _ =`.
let _ = self
.on_delete
.remove(&callback_id)
.expect("Attempt to remove non-existent delete callback");
}
pub(crate) fn remove_on_update(&mut self, callback_id: CallbackId) {
// Ugly: `impl FnMut` is `must_use`.
// If we don't `.expect` this, no diagnostic,
// but we want to assert that we actually removed a callback,View on GitHub (pinned to 524b4487d9)
Solutions
- Upgrade spacetimedb Rust SDK to the latest patch release; search the repo's issues for 'Attempt to remove non-existent insert callback'.
- Minimize unusual lifecycle churn (repeated register/unregister or re-init of the same client) until on a fixed version.
- If patching locally, downgrade the expect to a warning no-op remove and report the repro upstream.
Example fix
// before (SDK internal)
let _ = self.on_insert.remove(&callback_id).expect("Attempt to remove non-existent insert callback");
// after (defensive)
if self.on_insert.remove(&callback_id).is_none() {
tracing::warn!("on_insert callback {:?} already removed", callback_id);
} Defensive patterns
Strategy: validation
Validate before calling
// SDK users: if you maintain callback ids, never tear down the same handle twice;
// conceptually: if callbacks.contains_key(&id) { remove_on_insert(id); } Try / catch
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| client.disconnect())); if r.is_err() { /* SDK bug: capture panic, recreate the client */ } Prevention
- Upgrade the SDK when callback lifecycle fixes land.
- Don't run cleanup paths (disconnect plus drop) repeatedly on the same client.
- Report occurrences with a minimal repro to the spacetimedb repo.
When it happens
Trigger: SDK-internal lifecycle paths (client recreation, subscription teardown, reducer lifecycle) calling remove_on_insert twice for the same CallbackId, or removing after a registration path failed — typically under async races during disconnect/reconnect.
Common situations: Upgrading the spacetimedb Rust SDK and hitting a lifecycle regression; rapidly disconnecting/reconnecting a client; teardown racing a subscription update callback.
Related errors
- Attempt to remove non-existent delete callback
- never types are not yet supported in C# output
- unions not supported
- must specify table accessor, e.g. `#[spacetimedb::table(acce
- a direct index must be paired with a `#[unique] constraint
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/1d15a7347922216d.
Report an issue: GitHub.