xai-org/grok-build · error
terminal writer did not drain before suspend
Error message
terminal writer did not drain before suspend
What it means
An io::Error of kind TimedOut from suspend_for_child when wait_drained(750ms) on the synchronous terminal writer returns WriterDrain::TimedOut — pending output bytes were not flushed to the tty before suspending the app for a child (e.g. an external editor). The input pause flag is restored and suspend is aborted so buffered output isn't interleaved with the child's screen.
Source
Thrown at crates/codegen/xai-grok-pager/src/app/event_loop.rs:381
input_paused: &std::sync::atomic::AtomicBool,
reader_parked: &std::sync::atomic::AtomicBool,
input_rx: &mut tokio::sync::mpsc::UnboundedReceiver<TimedInputEvent>,
run_child: impl FnOnce(),
) -> std::io::Result<Option<(u16, u16)>> {
use std::sync::atomic::Ordering;
if !park_input_reader(input_paused, reader_parked, Duration::from_millis(500)) {
input_paused.store(false, Ordering::Release);
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"terminal input reader did not park before suspend",
));
}
let writer_sync = terminal.backend_mut().writer_mut().writer_sync().clone();
match writer_sync.wait_drained(Duration::from_millis(750)) {
Ok(crate::render::draw::WriterDrain::Drained) => {}
Ok(crate::render::draw::WriterDrain::TimedOut) => {
input_paused.store(false, Ordering::Release);
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"terminal writer did not drain before suspend",
));
}
Err(error) => {
input_paused.store(false, Ordering::Release);
return Err(error);
}
}
// Pre-child cursor probe (minimal only; minimal's startup already proved this terminal answers CPR)
// Reader is parked, so the reply is ours
let pre_cursor = screen_mode
.is_minimal()
.then(|| crossterm::cursor::position().ok())
.flatten();
// Fullscreen stays on the alternate screen
// A full-screen child (editor / pager) draws over it directly, so the primary screen (the user's shell) never flashes while the child spawnsView on GitHub (pinned to bc7f02eddd)
Solutions
- Retry the suspend once the writer has caught up
- Reduce the amount of pending output before handoff (repaint minimally)
- Check tty/pty health and network latency to the terminal
- Increase the 750ms drain window for slow links
Defensive patterns
Strategy: retry
Try / catch
match suspend_for_child(...) {
Err(e) if e.kind() == std::io::ErrorKind::TimedOut
&& e.to_string().contains("did not drain") => retry_after_flush(),
other => other,
} Prevention
- Flush/minimize pending render output before editor handoff
- Test suspend flows over slow SSH links
- Allow a larger drain window on high-latency ttys
When it happens
Trigger: Large pending render output or a slow/blocked tty (SSH link, pty) preventing the writer from draining within 750 ms before a child process takes over the terminal.
Common situations: Opening an editor over a laggy SSH session; rendering a very large transcript right before editor handoff; pty buffer pressure.
Related errors
- terminal input reader did not park before suspend
- wait failed: {body}
- WebSocket connection timed out after {} seconds
- GCS download stalled: no data received for {chunk_timeout:?}
- command timed out after {}s
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/2857cc0743eb794e.
Report an issue: GitHub.