Hmbown/CodeWhale · error · anyhow::Error
Failed to capture stdout
Error message
Failed to capture stdout
What it means
When a spawned child is handled with separated stdout/stderr reader threads (the non-combined, non-bounded path), child.stdout must be Some — i.e. the child was spawned with piped stdout. If stdout was not piped (take() yields None), the already-spawned child is terminated (terminate_unregistered_process, plus Windows job cleanup) and this error returns. It is an internal invariant: output capture was assumed but the spawn did not provide the pipe.
Source
Thrown at crates/tui/src/tools/shell.rs:2538
{
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 0c42157ee5)
Solutions
- Retry the command once — transient spawn misconfiguration is unlikely but cheap to rule out.
- If reproducible, report upstream with the exact command, background/tty flags, and platform.
- As a workaround, run the command in background mode with bounded output, which uses the combined reader path.
Defensive patterns
Strategy: retry
Try / catch
let out = match exec_shell(cmd, dir, opts).await {
Ok(o) => o,
Err(e) if e.to_string().contains("Failed to capture stdout") => {
// reader-thread invariant broke; retry once, then fall back to bounded/background mode
exec_shell(cmd, dir, opts).await
.or_else(|_| exec_shell_background_bounded(cmd, dir))?
}
Err(e) => return Err(e),
}; Prevention
- Prefer background execution with bounded output for commands whose capture path you control.
- If it reproduces with one specific command/flag combination, report it — this path assumes piped stdout at spawn.
When it happens
Trigger: A spawn path or platform behavior where the child is created without Stdio::piped() stdout but the separated-reader branch is taken; e.g. an internal regression or a mode mismatch between requested capture and spawn configuration.
Common situations: Rare; users normally cannot trigger it via the tool API — output capture is requested implicitly. Would surface after code changes to spawn options.
Related errors
- Failed to capture stderr
- stdin is not available for task {}
- Persistent service has no process group id
- credential handoff could not write to stdout
- tmux new-session failed with {status}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/1cb48d4f81df1671.
Report an issue: GitHub.