Hmbown/CodeWhale · error
PR stdout reader failed
Error message
PR stdout reader failed
What it means
`run_command` captures child stdout on a dedicated reader thread; after the process exits it `join()`s that thread. If the reader thread panicked, `join()` returns `Err`, and the tool converts that into "PR stdout reader failed" so no partial output is accepted. It indicates an internal capture failure, not a command failure.
Solutions
- Retry the command — a transient panic in the reader may not recur
- Check available memory; large diffs/outputs can stress the capture buffer
- Reduce subprocess output size (narrow the diff or git command) and re-run
- If reproducible, report/inspect the reader-thread panic — it is an internal bug
Defensive patterns
Strategy: retry
Try / catch
match run_command(ws, Program::Gh, &args) { Err(e) if e.to_string().contains("stdout reader failed") => run_with_retry(ws, Program::Gh, &args, 2), Err(e) => Err(e), Ok(v) => Ok(v) } Prevention
- Retry bounded subprocess capture on transient reader failures
- Keep subprocess output within MAX_OUTPUT_BYTES to reduce reader stress
- Watch for reproducible panics — they indicate a bug in the reader threads, not your inputs
When it happens
Trigger: The stdout reader thread panics (e.g. allocation failure on an unexpectedly huge stream, mutex poisoning, or a bug in the reader loop) while `fetch_view`, `fetch_diff`, `context_blob`, or `git` runs a subprocess.
Common situations: Extremely large subprocess output stressing the reader; a poisoned shared buffer after an earlier panic; running in a memory-constrained environment.
Related errors
- PR stderr reader failed
- AT-SPI action failed
- Cannot open session : its queued input is already open in…
- cargo metadata failed with exit code
- Checkpoint schema v is newer than supported v
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f50b50ea71e91455.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/review_pr.rs:499
.take()
.context("PR command stdout unavailable")?;
let stderr = child
.stderr
.take()
.context("PR command stderr unavailable")?;
let stdout = std::thread::spawn(move || read_bounded(stdout, MAX_OUTPUT_BYTES));
let stderr = std::thread::spawn(move || read_bounded(stderr, 64 * 1024));
let status = match child.wait_timeout(Duration::from_secs(60))? {
Some(status) => status,
None => {
let _ = child.kill();
let _ = child.wait();
bail!("PR input command timed out; no partial output was accepted");
}
};
let stdout = stdout
.join()
.map_err(|_| anyhow::anyhow!("PR stdout reader failed"))??;
let stderr = stderr
.join()
.map_err(|_| anyhow::anyhow!("PR stderr reader failed"))??;
if stdout.len() > MAX_OUTPUT_BYTES || stderr.len() > 64 * 1024 {
bail!(
"PR input exceeds the bounded capture limit (8 MiB diff); no partial output was accepted"
);
}
if !status.success() {
bail!(
"PR input command failed: {}",
String::from_utf8_lossy(&stderr).trim()
);
}
String::from_utf8(stdout).context("PR diff is not valid UTF-8; no lossy review is accepted")
}
#[cfg(test)]View on GitHub (pinned to 73e0f67d83)