{"record":{"id":"9094c5c1ead6f2cd","repo":"FyroxEngine/Fyrox","slug":"failed-to-send-script-message-it-means-the-scene","errorCode":null,"errorMessage":"Failed to send script message, it means the scene is already deleted!","messagePattern":"Failed to send script message, it means the scene is already deleted!","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"fyrox-impl/src/script/mod.rs","lineNumber":153,"sourceCode":"}\n\n/// A script message sender.\n#[derive(Clone)]\npub struct ScriptMessageSender {\n    pub(crate) sender: Sender<ScriptMessage>,\n}\n\nimpl Debug for ScriptMessageSender {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"ScriptMessageSender\")\n    }\n}\n\nimpl ScriptMessageSender {\n    /// Send a generic script message.\n    pub fn send(&self, message: ScriptMessage) {\n        if self.sender.send(message).is_err() {\n            Log::err(\"Failed to send script message, it means the scene is already deleted!\");\n        }\n    }\n\n    /// Sends a targeted script message with the given payload.\n    pub fn send_to_target<T>(&self, target: Handle<impl ObjectOrVariant<Node>>, payload: T)\n    where\n        T: ScriptMessagePayload,\n    {\n        self.send(ScriptMessage {\n            payload: Box::new(payload),\n            kind: ScriptMessageKind::Targeted(target.transmute()),\n        })\n    }\n\n    /// Sends a global script message with the given payload.\n    pub fn send_global<T>(&self, payload: T)\n    where\n        T: ScriptMessagePayload,","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/FyroxEngine/Fyrox/blob/76c91aad8eca488ce527b1af707be8b3b24ad72d/fyrox-impl/src/script/mod.rs#L135-L171","documentation":"ScriptMessageSender wraps a channel whose receiver lives in a scene's script context. When the receiver has been dropped (scene deleted) the mpsc send fails; the engine logs this error and silently drops the message — the caller gets no error back.","triggerScenarios":"Calling ScriptMessageSender::send / send_to_target / send_global / send_hierarchical after the owning scene was removed or is being destroyed.","commonSituations":"Deferred or queued script messages delivered after scene unload; background tasks or UI callbacks firing into a scene handle that no longer exists; sending during teardown ordering races.","solutions":["Check that the scene is still alive before sending script messages","Drop or invalidate cached ScriptMessageSender references when the scene is destroyed","Defer teardown-sensitive messages until next frame and verify scene validity","Treat the log line as a signal to audit scene lifetime management"],"exampleFix":"// before\nscript_sender.send_global(message); // scene may be gone\n// after\nif scene_handle.is_some() && engine.scenes.is_valid_handle(scene_handle) {\n    script_sender.send_global(message);\n}","handlingStrategy":"type-guard","validationCode":"if !engine.scenes().is_valid_handle(scene_handle) { return; } // then send\nscript_sender.send_global(msg);","typeGuard":"fn scene_alive(engine: &Engine, h: Handle<Scene>) -> bool { engine.scenes.is_valid_handle(h) }","tryCatchPattern":"// send() does not return Result; watch logs instead\nif !scene_alive(&engine, scene_handle) {\n    // skip message or re-queue after scene rebuild\n} else {\n    script_sender.send_global(msg);\n}","preventionTips":["Invalidate cached script senders on scene destruction","Check scene validity before deferred sends","Avoid sending script messages during teardown/shutdown paths"],"tags":["scripting","scene-lifetime","channel"],"backgroundTag":"broken-pipe","analyzedSha":"76c91aad8eca488ce527b1af707be8b3b24ad72d","analyzedAt":"2026-09-10T16:04:01.633Z","contentChangedAt":"2026-09-10T16:04:01.633Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}