{"record":{"id":"19eb9592acb7515c","repo":"Hmbown/CodeWhale","slug":"terminal-input-pump-disconnected","errorCode":null,"errorMessage":"terminal input pump disconnected","messagePattern":"terminal input pump disconnected","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"critical","filePath":"crates/tui/src/tui/ui.rs","lineNumber":552,"sourceCode":"            let remaining = deadline.saturating_duration_since(Instant::now());\n            match self.rx.recv_timeout(remaining) {\n                Ok(TerminalInputMessage::Event(event)) => {\n                    self.mark_alive();\n                    return Ok(Some(event));\n                }\n                Ok(TerminalInputMessage::Heartbeat) => {\n                    self.mark_alive();\n                    if remaining.is_zero() {\n                        return Ok(None);\n                    }\n                }\n                Ok(TerminalInputMessage::Error(err)) => {\n                    self.mark_alive();\n                    return Err(err);\n                }\n                Err(std::sync::mpsc::RecvTimeoutError::Timeout) => return Ok(None),\n                Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {\n                    return Err(io::Error::new(\n                        io::ErrorKind::BrokenPipe,\n                        \"terminal input pump disconnected\",\n                    ));\n                }\n            }\n        }\n    }\n\n    fn try_recv(&self) -> io::Result<Option<Event>> {\n        loop {\n            match self.rx.try_recv() {\n                Ok(TerminalInputMessage::Event(event)) => {\n                    self.mark_alive();\n                    return Ok(Some(event));\n                }\n                Ok(TerminalInputMessage::Heartbeat) => {\n                    self.mark_alive();\n                }","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tui/ui.rs#L534-L570","documentation":"BrokenPipe returned by the terminal input reader when the std::sync::mpsc channel's sender is dropped: the background thread that pumps terminal stdin into the UI (the 'input pump') has exited, so no further keyboard events can ever arrive. This is a fatal condition for the event loop, not a transient hiccup — after the sender is gone the channel can only report Disconnected.","triggerScenarios":"The input pump thread terminated — it panicked (e.g. a terminal library error it did not tolerate), was torn down during shutdown races, or the process is closing terminal fd 0 — and the UI event loop then hits RecvTimeoutError::Disconnected.","commonSituations":"A pump-thread panic triggered by an exotic terminal (mux wrappers, piped stdin, CI pseudo-TTYs); app shutdown where the pump is joined before the UI loop stops; bugs in terminal library initialization; terminal closed underneath a still-running UI.","solutions":["Restart the application — the pump thread cannot be revived from the UI loop","Reproduce with the same terminal setup (tmux/ssh/mux wrapper) and capture logs for a pump-thread panic","If it happens at exit only, it is a shutdown ordering race — update to a newer build and report the exact sequence if it persists","Avoid running the interactive TUI with stdin piped/redirected instead of a real TTY"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_input_pump_disconnected(e: &std::io::Error) -> bool {\n    e.kind() == std::io::ErrorKind::BrokenPipe && e.to_string().contains(\"input pump disconnected\")\n}","tryCatchPattern":"match ui.next_event(timeout) {\n    Ok(ev) => handle(ev),\n    Err(e) if is_input_pump_disconnected(&e) => {\n        tracing::error!(\"terminal input pump died; shutting down UI\");\n        graceful_shutdown(ExitCode::FAILURE) // no recovery: input is permanently lost\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Always run the interactive TUI attached to a real TTY, never with piped stdin","Capture pump-thread panics in logs so a disconnect has a diagnosable cause","On disconnect, exit cleanly instead of looping on a dead channel"],"tags":["terminal","input-pump","broken-pipe","threading","tui","rust"],"backgroundTag":"input-thread-disconnected","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}