zellij-org/zellij · critical
failed to receive event on channel
Error message
failed to receive event on channel
What it means
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.
Source
Thrown at zellij-server/src/pty.rs:216
pub(crate) struct Pty {
pub active_panes: HashMap<ClientId, PaneId>,
pub bus: Bus<PtyInstruction>,
pub id_to_child_pid: HashMap<u32, u32>, // terminal_id => child pid
originating_plugins: HashMap<u32, OriginatingPlugin>,
debug_to_file: bool,
task_handles: HashMap<u32, JoinHandle<()>>, // terminal_id to join-handle
default_editor: Option<PathBuf>,
post_command_discovery_hook: Option<String>,
plugin_cwds: HashMap<u32, PathBuf>, // plugin_id -> cwd
terminal_cwds: HashMap<u32, PathBuf>, // terminal_id -> cwd
pane_activity_flags: HashMap<u32, std::sync::Arc<std::sync::atomic::AtomicBool>>,
terminal_cmds: HashMap<u32, Vec<String>>,
terminal_foreground_cmds: HashMap<u32, Vec<String>>,
}
pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
loop {
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty((&event).into()));
match event {
PtyInstruction::SpawnTerminal(
terminal_action,
name,
new_pane_placement,
start_suppressed,
client_or_tab_index,
completion_tx,
set_blocking,
) => {
let err_context =
|| format!("failed to spawn terminal for {:?}", client_or_tab_index);
let (hold_on_close, run_command, pane_title, open_file_payload) =
match &terminal_action {
Some(TerminalAction::RunCommand(run_command)) => (
run_command.hold_on_close,View on GitHub (pinned to 98a0837077)
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
Example fix
// before
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
// after - exit the loop gracefully on disconnect
let (event, mut err_ctx) = match pty.bus.recv() {
Ok(recv) => recv,
Err(_) => {
log::info!("pty bus disconnected; exiting pty thread");
break;
}
}; Defensive patterns
Strategy: fallback
Try / catch
loop {
let (event, mut err_ctx) = match pty.bus.recv() {
Ok(recv) => recv,
Err(_) => {
log::info!("pty bus closed; stopping pty thread without panic");
break;
}
};
err_ctx.add_call(ContextType::Pty((&event).into()));
// handle event...
} Prevention
- 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
When it happens
Trigger: Session exit while SpawnTerminal/ClosePane instructions are still queued; an earlier server-thread panic disconnecting the bus under the pty loop.
Common situations: Rapid layout changes followed by exit; cascade after an unrelated server panic; shutdown sequencing changes between zellij versions.
Related errors
- failed to receive event on channel
- failed to spawn
- Could not find editor pane to replace - is no pane focused?
- Could not find editor pane to replace
- failed to find active pane id for client {client_id}
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/653e3d137e517191.
Report an issue: GitHub.