{"record":{"id":"6b1848d0838f8a78","repo":"DioxusLabs/dioxus","slug":"callback-was-called-after-the-runtime-was-dropped","errorCode":null,"errorMessage":"Callback was called after the runtime was dropped","messagePattern":"Callback was called after the runtime was dropped","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/events.rs","lineNumber":524,"sourceCode":"            Some(ExternalListenerCallback {\n                callback: Box::new(move |event: Args| f(event).spawn()),\n                runtime: Rc::downgrade(&runtime),\n            }),\n            Location::caller(),\n        );\n        Self { callback, origin }\n    }\n\n    /// Call this callback with the appropriate argument type\n    ///\n    /// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.\n    #[track_caller]\n    pub fn call(&self, arguments: Args) -> Ret {\n        if let Some(callback) = self.callback.write().as_mut() {\n            let runtime = callback\n                .runtime\n                .upgrade()\n                .expect(\"Callback was called after the runtime was dropped\");\n            let _guard = RuntimeGuard::new(runtime.clone());\n            runtime.with_scope_on_stack(self.origin, || (callback.callback)(arguments))\n        } else {\n            panic!(\"Callback was manually dropped\")\n        }\n    }\n\n    /// Create a `impl FnMut + Copy` closure from the Closure type\n    pub fn into_closure(self) -> impl FnMut(Args) -> Ret + Copy + 'static {\n        move |args| self.call(args)\n    }\n\n    /// Forcibly drop the internal handler callback, releasing memory\n    ///\n    /// This will force any future calls to \"call\" to not doing anything\n    pub fn release(&self) {\n        self.callback.set(None);\n    }","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/DioxusLabs/dioxus/blob/393d190a801ccb441d41923e232289b4f8a5c669/packages/core/src/events.rs#L506-L542","documentation":"A Callback stores a Weak handle to the Dioxus runtime (Rc<Runtime>) that existed when it was created. call() upgrades that Weak handle; if the VirtualDom/runtime has been dropped, upgrade returns None and this expect panics. It is Dioxus' guard against event handlers firing into a dead app.","triggerScenarios":"Invoking a Callback prop or a closure produced by into_closure() after the VirtualDom that created it was dropped: a JS addEventListener/setInterval/websocket handler still registered after unmount, a background thread or task that outlives the app, or a test that drops the vdom and then triggers events.","commonSituations":"Renderer torn down while external listeners (window events, timers, sockets) still hold the handler; passing a Callback into 'static or global state; integration tests firing events after the vdom goes out of scope.","solutions":["Detach external listeners and timers in component cleanup (use_effect cleanup / on unmount) before the runtime can be dropped","Keep the VirtualDom alive as long as anything can still fire its callbacks (store the vdom next to the handler)","In tests, trigger events while the vdom is still owned rather than after dropping it","Don't store Callbacks in global/static state; track app teardown with a flag and stop calling after it"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"// At an FFI/JS boundary where a stored handler may outlive the app:\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    stored_callback.call(args);\n}));\nif result.is_err() {\n    // runtime was dropped: unregister the listener and stop calling it\n    unregister_listener();\n}","preventionTips":["Unregister external listeners, timers and sockets in component cleanup before teardown","Tie the Callback's lifetime to the VirtualDom's: store both in the same owner","Never move a Callback into 'static state; wrap it in an Option your teardown code sets to None","In tests, trigger events while the vdom is still in scope"],"tags":["events","callback","lifetime","core","panic"],"backgroundTag":null,"analyzedSha":"393d190a801ccb441d41923e232289b4f8a5c669","analyzedAt":"2026-08-16T11:27:45.815Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}