xai-org/grok-build · error

terminal writer thread exited

Error message

terminal writer thread exited

What it means

During flush, the buffered terminal output is sent to a dedicated writer thread via a channel. If that thread has exited, send fails and a BrokenPipe io::Error with this message is produced; the failure is also recorded via mark_failed so sequence accounting stays consistent. Callers see flush fail rather than silently losing output.

Source

Thrown at crates/codegen/xai-grok-pager-render/src/render/draw.rs:223

    /// Shared writer progress used by the suspend path to
    /// [`WriterSync::wait_drained`] before a child takes the tty.
    pub fn writer_sync(&self) -> &WriterSync {
        &self.sync
    }
}
impl Write for TermWriter {
    fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
        self.buf.extend_from_slice(data);
        Ok(data.len())
    }
    fn flush(&mut self) -> std::io::Result<()> {
        if self.buf.is_empty() {
            return Ok(());
        }
        let sequence = self.sync.reserve_sequence();
        let data = std::mem::take(&mut self.buf);
        if self.tx.send(WriterPayload { sequence, data }).is_err() {
            let error = std::io::Error::new(
                std::io::ErrorKind::BrokenPipe,
                "terminal writer thread exited",
            );
            self.sync
                .mark_failed(std::io::Error::new(error.kind(), error.to_string()));
            return Err(error);
        }
        Ok(())
    }
}
impl Drop for TermWriter {
    fn drop(&mut self) {
        let _ = self.flush();
        self.sync.writer_active.store(false, Ordering::Release);
    }
}
/// Handle for the background writer thread.
///

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Ensure the writer/pager is not used after shutdown; drop order should end the writer last
  2. Check writer-thread logs for a panic or stderr write failure and fix the root cause
  3. Re-create the renderer/terminal session if continued output is required
  4. Handle the BrokenPipe from flush as terminal and stop drawing

Example fix

// before
terminal.flush()?; // BrokenPipe if writer thread already exited
// after
match terminal.flush() {
    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { /* session torn down */ }
    other => other?,
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before flushing, ensure the writer thread is still alive
if !terminal.writer_thread_alive() {
    eprintln!("terminal writer thread already exited; skipping flush");
    return;
}

Try / catch

match terminal.flush() {
    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
        // writer thread gone; tear down and stop drawing
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling flush (or dropping the writer via Drop, or write_payload) after the background writer thread panicked, returned, or was joined during teardown.

Common situations: Terminal teardown races where Drop runs while the writer thread is already gone; writer thread panic from an io error on stderr; double-shutdown of the renderer.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/c625c643c374e3ac. Report an issue: GitHub.