Hmbown/CodeWhale · error
Failed to capture stderr
Error message
Failed to capture stderr
What it means
Same failure class as the stdout case but for the child's stderr: after spawning a non-TTY child, `child.stderr.take()` returned None, so output capture is impossible. The tool kills the unregistered child immediately and errors out rather than running a process whose diagnostics would be silently lost.
Solutions
- Raise the file-descriptor limit (ulimit -n) and retry the command
- Re-run — transient pipe-creation failures may not recur
- Check that the sandbox/job-object configuration preserves stdio for children
- Investigate descriptor leaks in long-running sessions if it reproduces
Example fix
// caller-side mitigation: ensure fd headroom before spawning
// before
shell.run("cargo test", { "background": false })
// after
// raise fd limit (ulimit -n 4096) or free leaked descriptors, then retry the same call Defensive patterns
Strategy: retry
Validate before calling
// ensure fd headroom before spawning (stderr pipe needs an fd too) // ulimit -n should comfortably exceed current usage + per-child pipe fds
Try / catch
// treat it as transient spawn failure
match result {
Err(e) if e.to_string().contains("Failed to capture stderr") => retry_with_backoff(cmd, 2),
other => other,
} Prevention
- Raise the file-descriptor limit before large concurrent runs
- Audit for fd leaks (unjoined reader threads, unclosed handles)
- Verify sandbox/job configurations always allocate stdio for children
When it happens
Trigger: Spawning a synchronous non-TTY command where the OS failed to create the stderr pipe (fd exhaustion, resource limits, platform spawn anomaly), making `Child::stderr` None.
Common situations: File-descriptor exhaustion on busy systems; containers or sandboxes restricting pipe allocation; concurrent processes leaking descriptors.
Related errors
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/fbaa2a23777cec0d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/shell.rs:2723
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))),
)
};
(
ShellChild::Process(child),
stdin_handle,
stdout_thread,
stderr_thread,
)View on GitHub (pinned to 73e0f67d83)