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

  1. Upgrade the spacetimedb Rust SDK; check the issue tracker for 'Attempt to remove non-existent delete callback'.
  2. Avoid aggressive repeated unsubscribe/reconnect churn on one client while on a known-bad version.
  3. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/22512e78e4c072a7. Report an issue: GitHub.