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

  1. Verify stderr is a valid, open fd before rendering (avoid running after the parent closes it)
  2. Handle the propagated io error and tear down the pager session gracefully
  3. If stderr is redirected, redirect to a live sink (file or pty) instead of a closed pipe
  4. 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

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


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