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

  1. Restart the application — the pump thread cannot be revived from the UI loop
  2. Reproduce with the same terminal setup (tmux/ssh/mux wrapper) and capture logs for a pump-thread panic
  3. 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
  4. 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

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


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/19eb9592acb7515c. Report an issue: GitHub.