Hmbown/CodeWhale · error
Failed to capture stdout
Error message
Failed to capture stdout
What it means
When the tool spawns a child on the foreground path without a TTY, it takes the child's stdout handle to feed a reader thread. If `child.stdout` is None (the OS failed to give the process a stdout pipe, or it was already consumed), the tool cannot proceed: it terminates the just-spawned (unregistered) child and returns this error. It signals a broken pipe wiring between the parent and the spawned process rather than a problem with the command itself.
Solutions
- Check and raise the file-descriptor limit (ulimit -n) and retry — pipe creation failures usually come from fd exhaustion
- Re-run the command; transient pipe-setup failures may not recur
- Verify the sandbox/job configuration isn't stripping stdio from the child
- If reproducible, report/inspect how the child is spawned (std::process Command stdio wiring) for a platform bug
Example fix
// caller-side mitigation: run with a bounded fd budget and retry
// before
shell.run("make", { "background": false })
// after: raise fd limit first, then retry the same call
// (ulimit -n 4096) then retry the same call Defensive patterns
Strategy: retry
Validate before calling
// check fd headroom before spawning many processes
let ok = unsafe { libc::getrlimit(libc::RLIMIT_NOFILE) };
// ensure soft limit leaves room for pipes; raise ulimit -n if near the cap Try / catch
// treat it as transient spawn failure
match result {
Err(e) if e.to_string().contains("Failed to capture stdout") => retry_with_backoff(cmd, 2),
other => other,
} Prevention
- Keep the fd limit generous on systems running many concurrent shells
- Avoid leaking descriptors in long-lived agent sessions
- Review sandbox/job-object settings so children inherit stdio pipes
When it happens
Trigger: Spawning a synchronous sandboxed/non-TTY command whose `Child::stdout` is unexpectedly None after spawn — e.g. the OS denies pipe creation, resource exhaustion (fd limits), or a platform-level spawn quirk on Windows/Unix.
Common situations: Systems at or near the file-descriptor limit so pipe creation fails; sandbox/job-object setups interfering with stdio inheritance; resource-constrained containers.
Related errors
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/1cb48d4f81df1671.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/shell.rs:2716
{
windows_job = attach_windows_job(&child, original_command);
}
let stdin_handle = child.stdin.take().map(StdinWriter::Pipe);
let (stdout_thread, stderr_thread) =
if let (Some(reader), Some(output)) = (combined_reader, bounded_output.as_ref()) {
(
Some(spawn_bounded_reader_thread(reader, Arc::clone(output))),
None,
)
} else {
let stdout_handle = child.stdout.take().ok_or_else(|| {
#[cfg(windows)]
terminate_unregistered_process(&mut child, windows_job.as_ref());
#[cfg(not(windows))]
terminate_unregistered_process(&mut child);
anyhow!("Failed to capture stdout")
})?;
let stderr_handle = child.stderr.take().ok_or_else(|| {
#[cfg(windows)]
terminate_unregistered_process(&mut child, windows_job.as_ref());
#[cfg(not(windows))]
terminate_unregistered_process(&mut child);
anyhow!("Failed to capture stderr")
})?;
(
Some(spawn_reader_thread(
stdout_handle,
Arc::clone(&stdout_buffer),
)),
stderr_buffer
.as_ref()
.map(|buffer| spawn_reader_thread(stderr_handle, Arc::clone(buffer))),
)
};View on GitHub (pinned to 73e0f67d83)