Hmbown/CodeWhale · critical · std::io::Error
terminal input pump disconnected
Error message
terminal input pump disconnected
What it means
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.
Source
Thrown at crates/tui/src/tui/ui.rs:552
let remaining = deadline.saturating_duration_since(Instant::now());
match self.rx.recv_timeout(remaining) {
Ok(TerminalInputMessage::Event(event)) => {
self.mark_alive();
return Ok(Some(event));
}
Ok(TerminalInputMessage::Heartbeat) => {
self.mark_alive();
if remaining.is_zero() {
return Ok(None);
}
}
Ok(TerminalInputMessage::Error(err)) => {
self.mark_alive();
return Err(err);
}
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => return Ok(None),
Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"terminal input pump disconnected",
));
}
}
}
}
fn try_recv(&self) -> io::Result<Option<Event>> {
loop {
match self.rx.try_recv() {
Ok(TerminalInputMessage::Event(event)) => {
self.mark_alive();
return Ok(Some(event));
}
Ok(TerminalInputMessage::Heartbeat) => {
self.mark_alive();
}View on GitHub (pinned to 0c42157ee5)
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
Defensive patterns
Strategy: try-catch
Type guard
fn is_input_pump_disconnected(e: &std::io::Error) -> bool {
e.kind() == std::io::ErrorKind::BrokenPipe && e.to_string().contains("input pump disconnected")
} Try / catch
match ui.next_event(timeout) {
Ok(ev) => handle(ev),
Err(e) if is_input_pump_disconnected(&e) => {
tracing::error!("terminal input pump died; shutting down UI");
graceful_shutdown(ExitCode::FAILURE) // no recovery: input is permanently lost
}
Err(e) => Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- terminal input pump did not pause before launching editor
- unsupported locale '{other}'
- invalid locale '{value}'
- unsupported theme '{other}'
- invalid theme '{value}'
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/19eb9592acb7515c.
Report an issue: GitHub.