{"record":{"id":"6162740ba2136588","repo":"aaif-goose/goose","slug":"label-subprocess-exited-with-status","errorCode":null,"errorMessage":"{label} subprocess exited with status {}: {}","messagePattern":"(.+?) subprocess exited with status (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-cli/src/commands/review/orchestrator.rs","lineNumber":281,"sourceCode":"        .with_context(|| format!(\"spawn subprocess for {label}\"))?;\n\n    if let Some(mut stdin) = child.stdin.take() {\n        stdin\n            .write_all(prompt.as_bytes())\n            .await\n            .with_context(|| format!(\"write prompt to {label} stdin\"))?;\n        // Closing stdin signals EOF to `goose run -i -`.\n        drop(stdin);\n    }\n\n    let output = child\n        .wait_with_output()\n        .await\n        .with_context(|| format!(\"wait on {label}\"))?;\n\n    if !output.status.success() {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\n            \"{label} subprocess exited with status {}: {}\",\n            output.status,\n            truncate(&stderr, 500)\n        );\n    }\n\n    let stdout = String::from_utf8_lossy(&output.stdout);\n    parse_findings(&stdout)\n}\n\n/// Run the main correctness pass as N parallel subprocesses, one per\n/// touched file. This replaces the older in-process `session.headless()`\n/// path which:\n///\n/// 1. Streamed text-mode chatter to stdout (not JSONL) so findings were\n///    sometimes lost in interleaved output.\n/// 2. Sent the entire diff in a single prompt — large diffs (1000+\n///    lines) reliably caused Gemini 3.x to short-circuit with `[]`","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-cli/src/commands/review/orchestrator.rs#L263-L299","documentation":"goose review fans out its passes to child processes (`goose run -i -`, fed prompts over stdin). After wait_with_output(), a non-zero child exit status aborts the pass with this error, which embeds the exit status and up to 500 characters of the child's stderr (truncate).","triggerScenarios":"A review worker subprocess dying mid-run: provider auth failure (bad/expired key), rate limiting or 5xx from the model provider, or the child being OOM-killed in a constrained CI container.","commonSituations":"Large reviews hitting provider rate limits; credentials expiring between runs; memory-constrained CI killing workers; model outages surfacing as child failures.","solutions":["Read the embedded stderr snippet — it identifies the root cause (auth error, 429, crash)","Fix provider configuration via `goose configure` if the failure is auth-related","Retry after the rate-limit window; consider a smaller model or reduced parallelism","Narrow the review scope (`--range`, `--files`) to shorten worker lifetime","Raise memory/turn budgets in CI if the child was killed"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"# Bash: inspect exit status + stderr tail, retry only transient causes\nset +e\nOUT=$(goose review --files 'src/**' 2>&1); RC=$?\nset -e\nif [ \"$RC\" -ne 0 ]; then\n  printf '%s\\n' \"$OUT\" | tail -n 5\n  case \"$OUT\" in\n    *429*|*rate*|*529*) sleep 60; exec goose review --files 'src/**' ;;\n    *) exit \"$RC\" ;;\n  esac\nfi","preventionTips":["Pre-flight providers with `goose info --check` before long reviews","Chunk large reviews by directory to shorten worker lifetimes","Cap parallelism and turn budgets to stay under provider quotas","Persist worker stderr in CI logs so the 500-char snippet is never lost"],"tags":["review","subprocess","orchestration","provider","cli"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}