Hmbown/CodeWhale · error · anyhow::Error

Failed to capture stderr

Error message

Failed to capture stderr

What it means

Mirror of the stdout case: in the separated-stream path the child must have a piped stderr handle (child.stderr.take()); if None, the child is terminated and 'Failed to capture stderr' returns. Note that some modes (tty, persist_pending, small_contract) intentionally leave the stderr buffer unset, but the handle itself must still exist when this branch runs — None here means the spawn did not pipe stderr at all, an internal invariant break.

Source

Thrown at crates/tui/src/tools/shell.rs:2545

                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 0c42157ee5)

Solutions

  1. Retry the command once to rule out a transient spawn issue.
  2. If reproducible, report upstream with command, flags (background, tty, persist), and platform.
  3. Workaround: background execution with bounded/combined output avoids the separated stderr thread.
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 stderr") => {
        exec_shell(cmd, dir, opts).await
            .or_else(|_| exec_shell_background_bounded(cmd, dir))?
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Child spawned without Stdio::piped() stderr while the separated-reader path assumes it; internal regression or platform spawn quirk, not a caller-selectable condition.

Common situations: Rare; not triggerable through documented tool options. Would appear after modifying spawn stdio configuration.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/fbaa2a23777cec0d. Report an issue: GitHub.