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

  1. Check and raise the file-descriptor limit (ulimit -n) and retry — pipe creation failures usually come from fd exhaustion
  2. Re-run the command; transient pipe-setup failures may not recur
  3. Verify the sandbox/job configuration isn't stripping stdio from the child
  4. 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

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)