{"record":{"id":"653e3d137e517191","repo":"zellij-org/zellij","slug":"failed-to-receive-event-on-channel-653e3d","errorCode":null,"errorMessage":"failed to receive event on channel","messagePattern":"failed to receive event on channel","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"zellij-server/src/pty.rs","lineNumber":216,"sourceCode":"pub(crate) struct Pty {\n    pub active_panes: HashMap<ClientId, PaneId>,\n    pub bus: Bus<PtyInstruction>,\n    pub id_to_child_pid: HashMap<u32, u32>, // terminal_id => child pid\n    originating_plugins: HashMap<u32, OriginatingPlugin>,\n    debug_to_file: bool,\n    task_handles: HashMap<u32, JoinHandle<()>>, // terminal_id to join-handle\n    default_editor: Option<PathBuf>,\n    post_command_discovery_hook: Option<String>,\n    plugin_cwds: HashMap<u32, PathBuf>,   // plugin_id -> cwd\n    terminal_cwds: HashMap<u32, PathBuf>, // terminal_id -> cwd\n    pane_activity_flags: HashMap<u32, std::sync::Arc<std::sync::atomic::AtomicBool>>,\n    terminal_cmds: HashMap<u32, Vec<String>>,\n    terminal_foreground_cmds: HashMap<u32, Vec<String>>,\n}\n\npub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {\n    loop {\n        let (event, mut err_ctx) = pty.bus.recv().expect(\"failed to receive event on channel\");\n        err_ctx.add_call(ContextType::Pty((&event).into()));\n        match event {\n            PtyInstruction::SpawnTerminal(\n                terminal_action,\n                name,\n                new_pane_placement,\n                start_suppressed,\n                client_or_tab_index,\n                completion_tx,\n                set_blocking,\n            ) => {\n                let err_context =\n                    || format!(\"failed to spawn terminal for {:?}\", client_or_tab_index);\n\n                let (hold_on_close, run_command, pane_title, open_file_payload) =\n                    match &terminal_action {\n                        Some(TerminalAction::RunCommand(run_command)) => (\n                            run_command.hold_on_close,","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-server/src/pty.rs#L198-L234","documentation":"Identical failure mode in the pty thread: pty_thread_main loops on pty.bus.recv() and expects a PtyInstruction forever. recv() errors when all senders are gone - normally only during teardown ordering races or after another thread's panic dropped its sender end of the bus. Because the loop cannot proceed without instructions, the expect panics and takes the pty thread, and effectively the session, with it.","triggerScenarios":"Session exit while SpawnTerminal/ClosePane instructions are still queued; an earlier server-thread panic disconnecting the bus under the pty loop.","commonSituations":"Rapid layout changes followed by exit; cascade after an unrelated server panic; shutdown sequencing changes between zellij versions.","solutions":["Start a new session; the panicked one cannot be resumed reliably","Update zellij for pty-thread shutdown fixes","Check the log for an earlier panic - this error is usually the echo of another fault","Capture the sequence with debug logging if it reproduces and report it"],"exampleFix":"// before\nlet (event, mut err_ctx) = pty.bus.recv().expect(\"failed to receive event on channel\");\n\n// after - exit the loop gracefully on disconnect\nlet (event, mut err_ctx) = match pty.bus.recv() {\n    Ok(recv) => recv,\n    Err(_) => {\n        log::info!(\"pty bus disconnected; exiting pty thread\");\n        break;\n    }\n};","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"loop {\n    let (event, mut err_ctx) = match pty.bus.recv() {\n        Ok(recv) => recv,\n        Err(_) => {\n            log::info!(\"pty bus closed; stopping pty thread without panic\");\n            break;\n        }\n    };\n    err_ctx.add_call(ContextType::Pty((&event).into()));\n    // handle event...\n}","preventionTips":["Treat RecvError as end-of-stream, not as corruption","Join child threads before dropping bus senders during shutdown","Watch for prior panics in the same session before diagnosing the channel","Add integration tests around rapid spawn/exit cycles to catch ordering races"],"tags":["rust","zellij","channels","pty","shutdown","recv"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}