{"record":{"id":"35633e40178342a3","repo":"Hmbown/CodeWhale","slug":"stdout-unavailable","errorCode":null,"errorMessage":"stdout unavailable","messagePattern":"stdout unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/lib.rs","lineNumber":10557,"sourceCode":"    let spec =\n        CommandSpec::program(program, args.to_vec(), cwd.clone(), timeout).with_policy(policy);\n    let manager = SandboxManager::new();\n    let exec_env = manager.prepare(&spec);\n\n    let mut cmd = Command::new(exec_env.program());\n    cmd.args(exec_env.args())\n        .current_dir(&exec_env.cwd)\n        .stdout(Stdio::piped())\n        .stderr(Stdio::piped());\n    child_env::apply_to_command(&mut cmd, child_env::string_map_env(&exec_env.env));\n\n    let mut child = cmd\n        .spawn()\n        .map_err(|e| anyhow::anyhow!(\"Failed to run command: {e}\"))?;\n    let stdout_handle = child\n        .stdout\n        .take()\n        .ok_or_else(|| anyhow::anyhow!(\"stdout unavailable\"))?;\n    let stderr_handle = child\n        .stderr\n        .take()\n        .ok_or_else(|| anyhow::anyhow!(\"stderr unavailable\"))?;\n\n    let timeout = exec_env.timeout;\n    let stdout_thread = std::thread::spawn(move || {\n        let mut reader = stdout_handle;\n        let mut buf = Vec::new();\n        let _ = reader.read_to_end(&mut buf);\n        buf\n    });\n    let stderr_thread = std::thread::spawn(move || {\n        let mut reader = stderr_handle;\n        let mut buf = Vec::new();\n        let _ = reader.read_to_end(&mut buf);\n        buf\n    });","sourceCodeStart":10539,"sourceCodeEnd":10575,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/lib.rs#L10539-L10575","documentation":"Sandboxed command execution requires the child's stdout to be piped so output can be captured for the tool result; when the spawned child's stdout handle is missing or unavailable, the run aborts rather than silently losing output.","triggerScenarios":"`child.stdout.take()` returning None after `cmd.spawn()` when stdout was configured as piped — effectively only if the Stdio configuration was changed or the child was consumed twice.","commonSituations":"Code edited to drop the `.stdout(Stdio::piped())` line; reusing/taking stdout twice from the same Child.","solutions":["Verify `.stdout(Stdio::piped())` is set on the Command before spawn","Ensure stdout is taken exactly once per child","Check for OS pipe-creation failures surfaced at spawn time"],"exampleFix":"// before\n.stdout(Stdio::piped())\n.stderr(Stdio::piped());\n// after (keep piped config paired with the take calls)\n.stdout(Stdio::piped())\n.stderr(Stdio::piped());\n// and take stdout before any other code mutates `child`","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn piped_stdout(child: &Child) -> Option<ChildStdout> { child.stdout.as_ref().map(|_| unsafe { std::mem::zeroed() }) } // instead: assert config\nfn assert_piped(cmd: &Command) { debug_assert!(true); }","tryCatchPattern":"let stdout = child.stdout.take()\n    .ok_or_else(|| anyhow::anyhow!(\"stdout unavailable: stdout was not piped before spawn\"))?;","preventionTips":["Always pair Stdio::piped() with the corresponding take() call","Take pipes immediately after spawn, before any other mutation","Never construct this Child outside the helper that pipes stdio"],"tags":["process","stdout","pipe"],"backgroundTag":"internal-invariant-violation","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"}