Hmbown/CodeWhale · error

stderr unavailable

Error message

stderr unavailable

What it means

Thrown when `child.stderr` is None after spawning. Like the stdout case, this should be impossible while `.stderr(Stdio::piped())` is configured, so it flags a broken invariant or double-take of the pipe.

Solutions

  1. Verify `.stderr(Stdio::piped())` is set on the Command before spawn
  2. Take stderr exactly once per child
  3. If stderr is merged via Stdio::piped into stdout, remove the separate take

Example fix

// before
.stderr(Stdio::inherit()); // then child.stderr.take() -> None
// after
.stderr(Stdio::piped());
Defensive patterns

Strategy: try-catch

Try / catch

let stderr = child.stderr.take()
    .ok_or_else(|| anyhow::anyhow!("stderr unavailable: stderr was not piped before spawn"))?;

Prevention

When it happens

Trigger: `child.stderr.take()` returning None because stderr was not configured as piped or was already consumed.

Common situations: Someone removed the `.stderr(Stdio::piped())` configuration; stderr taken twice; merging stderr into stdout while still trying to take it separately.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/7edef7522177c75c. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/lib.rs:10561

    let mut cmd = Command::new(exec_env.program());
    cmd.args(exec_env.args())
        .current_dir(&exec_env.cwd)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    child_env::apply_to_command(&mut cmd, child_env::string_map_env(&exec_env.env));

    let mut child = cmd
        .spawn()
        .map_err(|e| anyhow::anyhow!("Failed to run command: {e}"))?;
    let stdout_handle = child
        .stdout
        .take()
        .ok_or_else(|| anyhow::anyhow!("stdout unavailable"))?;
    let stderr_handle = child
        .stderr
        .take()
        .ok_or_else(|| anyhow::anyhow!("stderr unavailable"))?;

    let timeout = exec_env.timeout;
    let stdout_thread = std::thread::spawn(move || {
        let mut reader = stdout_handle;
        let mut buf = Vec::new();
        let _ = reader.read_to_end(&mut buf);
        buf
    });
    let stderr_thread = std::thread::spawn(move || {
        let mut reader = stderr_handle;
        let mut buf = Vec::new();
        let _ = reader.read_to_end(&mut buf);
        buf
    });

    if let Some(status) = child.wait_timeout(timeout)? {
        let stdout = stdout_thread.join().unwrap_or_default();
        let stderr = stderr_thread.join().unwrap_or_default();

View on GitHub (pinned to 73e0f67d83)