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

  1. Retry the command — a transient panic in the reader may not recur
  2. Check available memory; large diffs/outputs can stress the capture buffer
  3. Reduce subprocess output size (narrow the diff or git command) and re-run
  4. 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

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


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)