{"record":{"id":"9202435cd5fa5da5","repo":"Hmbown/CodeWhale","slug":"pr-stderr-reader-failed","errorCode":null,"errorMessage":"PR stderr reader failed","messagePattern":"PR stderr reader failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/review_pr.rs","lineNumber":502,"sourceCode":"        .stderr\n        .take()\n        .context(\"PR command stderr unavailable\")?;\n    let stdout = std::thread::spawn(move || read_bounded(stdout, MAX_OUTPUT_BYTES));\n    let stderr = std::thread::spawn(move || read_bounded(stderr, 64 * 1024));\n    let status = match child.wait_timeout(Duration::from_secs(60))? {\n        Some(status) => status,\n        None => {\n            let _ = child.kill();\n            let _ = child.wait();\n            bail!(\"PR input command timed out; no partial output was accepted\");\n        }\n    };\n    let stdout = stdout\n        .join()\n        .map_err(|_| anyhow::anyhow!(\"PR stdout reader failed\"))??;\n    let stderr = stderr\n        .join()\n        .map_err(|_| anyhow::anyhow!(\"PR stderr reader failed\"))??;\n    if stdout.len() > MAX_OUTPUT_BYTES || stderr.len() > 64 * 1024 {\n        bail!(\n            \"PR input exceeds the bounded capture limit (8 MiB diff); no partial output was accepted\"\n        );\n    }\n    if !status.success() {\n        bail!(\n            \"PR input command failed: {}\",\n            String::from_utf8_lossy(&stderr).trim()\n        );\n    }\n    String::from_utf8(stdout).context(\"PR diff is not valid UTF-8; no lossy review is accepted\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/review_pr.rs#L484-L520","documentation":"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.","triggerScenarios":"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.","commonSituations":"Verbose git/gh error output on a failing command; memory pressure; a prior panic poisoning state shared with the reader threads.","solutions":["Retry the operation; transient reader panics usually do not repeat","Check what the underlying command writes to stderr and whether output volume can be reduced","Verify system memory available to the TUI process","If deterministic, treat as a bug in `run_command`'s reader threads and fix/report it"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"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) }","preventionTips":["Retry bounded subprocess capture on transient reader failures","Reduce noisy stderr from underlying git/gh commands","Investigate repeated occurrences as a run_command reader-thread bug"],"tags":["subprocess","io","thread","stderr","review-pr"],"backgroundTag":"thread-interrupted","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}