{"record":{"id":"1d15a7347922216d","repo":"clockworklabs/SpacetimeDB","slug":"attempt-to-remove-non-existent-insert-callback","errorCode":null,"errorMessage":"Attempt to remove non-existent insert callback","messagePattern":"Attempt to remove non-existent insert callback","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/rust/src/callbacks.rs","lineNumber":157,"sourceCode":"\n    pub(crate) fn register_on_delete(&mut self, callback_id: CallbackId, callback: RowCallback<M>) {\n        self.on_delete.insert(callback_id, callback);\n    }\n\n    pub(crate) fn register_on_update(&mut self, callback_id: CallbackId, callback: UpdateCallback<M>) {\n        self.on_update.insert(callback_id, callback);\n    }\n\n    pub(crate) fn remove_on_insert(&mut self, callback_id: CallbackId) {\n        // Ugly: `impl FnMut` is `must_use`.\n        // If we don't `.expect` this, no diagnostic,\n        // but we want to assert that we actually removed a callback,\n        // we just don't want to invoke it.\n        // So we have to `let _ =`.\n        let _ = self\n            .on_insert\n            .remove(&callback_id)\n            .expect(\"Attempt to remove non-existent insert callback\");\n    }\n\n    pub(crate) fn remove_on_delete(&mut self, callback_id: CallbackId) {\n        // Ugly: `impl FnMut` is `must_use`.\n        // If we don't `.expect` this, no diagnostic,\n        // but we want to assert that we actually removed a callback,\n        // we just don't want to invoke it.\n        // So we have to `let _ =`.\n        let _ = self\n            .on_delete\n            .remove(&callback_id)\n            .expect(\"Attempt to remove non-existent delete callback\");\n    }\n\n    pub(crate) fn remove_on_update(&mut self, callback_id: CallbackId) {\n        // Ugly: `impl FnMut` is `must_use`.\n        // If we don't `.expect` this, no diagnostic,\n        // but we want to assert that we actually removed a callback,","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/sdks/rust/src/callbacks.rs#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"Upgrading the spacetimedb Rust SDK and hitting a lifecycle regression; rapidly disconnecting/reconnecting a client; teardown racing a subscription update callback.","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."],"exampleFix":"// before (SDK internal)\nlet _ = self.on_insert.remove(&callback_id).expect(\"Attempt to remove non-existent insert callback\");\n\n// after (defensive)\nif self.on_insert.remove(&callback_id).is_none() {\n    tracing::warn!(\"on_insert callback {:?} already removed\", callback_id);\n}","handlingStrategy":"validation","validationCode":"// SDK users: if you maintain callback ids, never tear down the same handle twice;\n// conceptually: if callbacks.contains_key(&id) { remove_on_insert(id); }","typeGuard":null,"tryCatchPattern":"let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| client.disconnect())); if r.is_err() { /* SDK bug: capture panic, recreate the client */ }","preventionTips":["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."],"tags":["rust","spacetimedb-sdk","callbacks","client","double-remove"],"backgroundTag":"callback-already-removed","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}