Hmbown/CodeWhale · error

PR stderr reader failed

Error message

PR stderr reader failed

What it means

Identical to the stdout case but for stderr: `run_command` reads the child's stderr on its own thread and `join()`s it. A panicked or poisoned stderr reader thread becomes "PR stderr reader failed", aborting the bounded-capture contract rather than returning truncated diagnostics.

Solutions

  1. Retry the operation; transient reader panics usually do not repeat
  2. Check what the underlying command writes to stderr and whether output volume can be reduced
  3. Verify system memory available to the TUI process
  4. If deterministic, treat as a bug in `run_command`'s reader threads and fix/report it
Defensive patterns

Strategy: retry

Try / catch

match run_command(ws, Program::Git, &args) { Err(e) if e.to_string().contains("stderr reader failed") => run_with_retry(ws, Program::Git, &args, 2), Err(e) => Err(e), Ok(v) => Ok(v) }

Prevention

When it happens

Trigger: The stderr reader thread panics during `fetch_view`, `fetch_diff`, `context_blob`, or `git` subprocess execution; typically after a huge or adversarial stderr stream or a poisoned shared buffer.

Common situations: Verbose git/gh error output on a failing command; memory pressure; a prior panic poisoning state shared with the reader threads.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/9202435cd5fa5da5. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/review_pr.rs:502

        .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)]
mod tests {
    use super::*;

View on GitHub (pinned to 73e0f67d83)