{"record":{"id":"4b735172a91423eb","repo":"zellij-org/zellij","slug":"failed-to-send-message-to-channel","errorCode":null,"errorMessage":"failed to send message to channel: {:#?}","messagePattern":"failed to send message to channel: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"zellij-utils/src/errors.rs","lineNumber":956,"sourceCode":"    }\n\n    /// `SendError` doesn't satisfy `anyhow`s trait requirements due to `T` possibly being a\n    /// `PluginInstruction` type, which wraps an `mpsc::Send` and isn't `Sync`. Due to this, in turn,\n    /// the whole error type isn't `Sync` and doesn't work with `anyhow` (or pretty much any other\n    /// error handling crate).\n    ///\n    /// Takes the `SendError` and creates an `anyhow` error type with the message that was sent\n    /// (formatted as string), attaching the [`ErrorContext`] as anyhow context to it.\n    impl<T: std::fmt::Debug, U> ToAnyhow<U>\n        for Result<U, crate::channels::SendError<(T, ErrorContext)>>\n    {\n        fn to_anyhow(self) -> anyhow::Result<U> {\n            match self {\n                Ok(val) => anyhow::Ok(val),\n                Err(e) => {\n                    let (msg, context) = e.into_inner();\n                    if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {\n                        Err(anyhow::anyhow!(\n                            \"failed to send message to channel: {:#?}\",\n                            msg\n                        ))\n                        .with_context(|| context.to_string())\n                    } else {\n                        Err(anyhow::anyhow!(\"failed to send message to channel\"))\n                            .with_context(|| context.to_string())\n                    }\n                },\n            }\n        }\n    }\n\n    impl<U> ToAnyhow<U> for Result<U, std::sync::PoisonError<U>> {\n        fn to_anyhow(self) -> anyhow::Result<U> {\n            match self {\n                Ok(val) => anyhow::Ok(val),\n                Err(e) => {","sourceCodeStart":938,"sourceCodeEnd":974,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-utils/src/errors.rs#L938-L974","documentation":"Debug-mode variant of the ToAnyhow implementation for crossbeam/mpc SendError in zellij-utils/errors.rs. Sending a (T, ErrorContext) message failed because the receiving end of the channel was dropped — typically the owning thread exited via shutdown, disconnect, or a panic. In DEBUG_MODE the unsent message itself is pretty-printed into the error before the ErrorContext is attached, so you can see exactly which message could not be delivered.","triggerScenarios":"Calling .to_anyhow()? (directly or via the ? operator) on a channel send after the receiver thread has terminated: session teardown while another thread still emits actions, a panicked screen/router/os_input worker that owned the receiver, or a bus/IPC endpoint disconnecting mid-send.","commonSituations":"Zellij server shutting down while a plugin or scheduled action fires; a worker thread that panicked earlier (this error is the downstream symptom); killing the session process during heavy keybinding/plugin activity; races between client disconnect and pending input routing.","solutions":["Look earlier in the logs for a panic or exit in the thread that owned the channel receiver — this error is almost always the symptom, not the cause","Restart the zellij session (`zellij --new-session ...`) to rebuild the channel fabric","Avoid force-killing the server while plugins are scheduling actions; use the quit action for graceful teardown","Upgrade zellij if the receiver-thread panic is a known fixed bug; report with the panic backtrace if not"],"exampleFix":"// before\nsender.send((msg, ErrorContext::new())).to_anyhow()?; // errors after receiver dies\n\n// after: stop sending once the peer is gone\nif !sender.is_connected_to_any_receiver() {\n    log::warn!(\"dropping message, receiver gone\");\n    return Ok(());\n}\nsender.send((msg, ErrorContext::new())).to_anyhow()?;","handlingStrategy":"try-catch","validationCode":"// before sending on a crossbeam bus sender\nif !sender.is_connected_to_any_receiver() {\n    // receiver thread is gone; sending would fail\n    return Ok(());\n}","typeGuard":null,"tryCatchPattern":"use zellij_utils::errors::prelude::*;\n\nif let Err(e) = sender.send((msg, ErrorContext::new())).to_anyhow() {\n    log::warn!(\"dropping message, channel closed: {e:#}\");\n    // treat as termination of this emitter loop, do not propagate\n    break;\n}","preventionTips":["Design senders to stop (break their loop) on the first SendError instead of retrying sends","Establish shutdown ordering: producers stop before consumers are dropped","Check is_connected_to_any_receiver() before sends in teardown paths","Never treat a send failure as the root cause — always hunt the earlier receiver panic/exit in logs"],"tags":["channel","concurrency","shutdown","ipc","send-error"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}