clockworklabs/SpacetimeDB · error
Attempt to remove non-existent delete callback
Error message
Attempt to remove non-existent delete callback
What it means
The Rust client SDK tracks on_delete callbacks by CallbackId in an internal map; `remove_on_delete` expects the id to be present and panics when `remove` returns None. The method is pub(crate), so users see this only through SDK internals double-removing or removing an unregistered id — an SDK bug or version regression rather than user callback misuse.
Source
Thrown at sdks/rust/src/callbacks.rs:169
// 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,
// we just don't want to invoke it.
// So we have to `let _ =`.
let _ = self
.on_update
.remove(&callback_id)
.expect("Attempt to remove non-existent update callback");
}
fn invoke_on_insert(&mut self, ctx: &M::EventContext, row: &dyn Any) {
for callback in self.on_insert.values_mut() {
callback(ctx, row);
}View on GitHub (pinned to 524b4487d9)
Solutions
- Upgrade the spacetimedb Rust SDK; check the issue tracker for 'Attempt to remove non-existent delete callback'.
- Avoid aggressive repeated unsubscribe/reconnect churn on one client while on a known-bad version.
- Patch locally by making the remove tolerant (log instead of panic) and report the repro.
Example fix
// before (SDK internal)
let _ = self.on_delete.remove(&callback_id).expect("Attempt to remove non-existent delete callback");
// after (defensive)
if self.on_delete.remove(&callback_id).is_none() {
tracing::warn!("on_delete callback {:?} already removed", callback_id);
} Defensive patterns
Strategy: validation
Validate before calling
// SDK users: guard against double teardown of the same callback handle before unsubscribing
Try / catch
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| client.disconnect())); if r.is_err() { /* SDK bug: log panic, rebuild client state */ } Prevention
- Avoid rapid unsubscribe/reconnect churn on one client instance.
- Update the SDK promptly; these pub(crate) expects are internal invariants.
- File an issue with the panic backtrace and lifecycle sequence when hit.
When it happens
Trigger: SDK-internal teardown paths (disconnect, subscription replacement, client drop races) invoking remove_on_delete twice for the same CallbackId or after a failed registration.
Common situations: SDK upgrade regressions; rapid unsubscribe/reconnect cycles; teardown racing an in-flight subscription update that touches on_delete handlers.
Related errors
- Attempt to remove non-existent insert 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/22512e78e4c072a7.
Report an issue: GitHub.