xai-org/grok-build · error
{error}
Error message
{error} What it means
write_payload propagates the underlying io error from the writer thread's stderr write. Before returning Err it records the failure via sync.mark_failed (preserving kind and message, rendered into the message as {error}), so pending sequence accounting marks the frame as failed. The displayed message is simply the original io error text.
Source
Thrown at crates/codegen/xai-grok-pager-render/src/render/draw.rs:286
let _ = h.join();
}
}
}
fn write_payload(
writer: &mut impl Write,
payload: &WriterPayload,
sync: &WriterSync,
) -> std::io::Result<()> {
match writer
.write_all(&payload.data)
.and_then(|()| writer.flush())
{
Ok(()) => {
sync.mark_written(payload.sequence);
Ok(())
}
Err(error) => {
sync.mark_failed(std::io::Error::new(error.kind(), error.to_string()));
Err(error)
}
}
}
/// Spawn a background OS thread that writes frame data to stderr.
///
/// Returns the frame sender, shared writer state, completion-event receiver,
/// and the thread handle that must be joined during terminal teardown.
pub fn spawn_writer_thread() -> (
WriterSender,
WriterSync,
tokio::sync::mpsc::UnboundedReceiver<WriterEvent>,
WriterThread,
) {
let (tx, rx) = mpsc::channel::<WriterPayload>();
let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel();
let sync = WriterSync::with_event_sender(event_tx);
let thread_sync = sync.clone();View on GitHub (pinned to bc7f02eddd)
Solutions
- Verify stderr is a valid, open fd before rendering (avoid running after the parent closes it)
- Handle the propagated io error and tear down the pager session gracefully
- If stderr is redirected, redirect to a live sink (file or pty) instead of a closed pipe
- Check mark_failed consumers to surface the failure to users instead of hanging on acknowledgements
Example fix
// before
writer.write_payload(frame)?; // raw io error propagates
// after
if let Err(e) = writer.write_payload(frame) {
eprintln!("render failed ({}): stopping pager", e);
return Err(e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure stderr is writable before spawning the renderer
if std::io::stderr().write_all(b"").is_err() {
eprintln!("stderr unavailable; terminal rendering disabled");
return;
} Try / catch
match writer.write_payload(payload) {
Err(e) => {
eprintln!("frame write failed ({}): aborting render", e);
session.teardown();
}
Ok(()) => {}
} Prevention
- Do not close stderr while the pager is rendering
- Avoid redirecting stderr to pipes that the parent may close
- Surface mark_failed state to users instead of waiting on acks
- Test rendering over SSH/disconnect scenarios
When it happens
Trigger: Calling write_payload (invoked from spawn_writer_thread's loop) when writing the frame to stderr fails, e.g. stderr closed, EBADF, or the tty disappeared.
Common situations: Terminal/SSH session disconnecting mid-render; output redirection to a closed pipe; pager running after the parent process closed stderr.
Related errors
- terminal writer thread exited
- invalid editor command
- Failed to set working directory to {:?}: {}
- terminal writer stopped
- Failed to load config: {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/8ca5a7426b2f6822.
Report an issue: GitHub.