Hmbown/CodeWhale · warning

stderr unavailable

Error message

stderr unavailable

What it means

Defensive invariant on the stderr pipe, symmetric to the stdout one: .stderr(Stdio::piped()) is set before spawn, so child.stderr.take() must return Some. It cannot fire through normal usage; it indicates a regression where the stderr pipe was not requested or the handle was already consumed.

Source

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

    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 8880682c63)

Solutions

  1. Confirm .stderr(Stdio::piped()) is set on the Command before spawn
  2. Ensure the stderr handle is taken exactly once, in the same place as the stdout take
  3. Restore the unmodified spawn block if the file was patched for debugging

Example fix

// before (broken fork): stderr inherited
let mut cmd = Command::new(exec_env.program());
// after
let mut cmd = Command::new(exec_env.program());
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
Defensive patterns

Strategy: try-catch

Try / catch

// Treat as an internal invariant breach: report and abort, do not retry
match child.stderr.take() {
    Some(handle) => handle,
    None => return Err(anyhow::anyhow!(
        "internal error: stderr pipe missing after piped spawn (report upstream)"
    )),
}

Prevention

When it happens

Trigger: Not reachable via public inputs in the current source. Appears when a fork removes .stderr(Stdio::piped()), makes it conditional, or takes the stderr handle earlier (for example, to route stderr through a different reader).

Common situations: Debug patches that let stderr leak to the terminal, refactors that move pipe handling into a helper, double take() after restructuring.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/7edef7522177c75c. Report an issue: GitHub.