Hmbown/CodeWhale · error · anyhow::Error

stdin is not available for task {}

Error message

stdin is not available for task {}

What it means

BackgroundShell::write_stdin writes to a running background shell's stdin. The happy path requires self.stdin to be Some; an early return covers flush/close bookkeeping, and an empty input with close=true is a no-op. Reaching the final Err means the caller asked to write (or write+close) but the task has no live stdin stream — the process was spawned without a captured stdin or its stdin was already closed by a prior write with close=true.

Source

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

    fn write_stdin(&mut self, input: &str, close: bool) -> Result<()> {
        if let Some(stdin) = self.stdin.as_mut() {
            if !input.is_empty() {
                stdin
                    .write_all(input.as_bytes())
                    .context("Failed to write to stdin")?;
                stdin.flush().ok();
            }
            if close {
                self.stdin = None;
            }
            return Ok(());
        }

        if input.is_empty() && close {
            return Ok(());
        }

        Err(anyhow!("stdin is not available for task {}", self.id))
    }

    fn full_output(&self) -> (String, String, usize, usize) {
        if let Some(snapshot) = self.bounded_output_snapshot(false).ok().flatten() {
            return (snapshot.content, String::new(), snapshot.total_bytes, 0);
        }
        let (stdout_bytes, stderr_bytes, stdout_omitted, stderr_omitted) =
            self.retained_output_bytes_with_omissions();
        // Report what the stream produced, not what is still held.
        let stdout_len = stdout_bytes.len().saturating_add(stdout_omitted);
        let stderr_len = stderr_bytes.len().saturating_add(stderr_omitted);

        (
            String::from_utf8_lossy(&stdout_bytes).to_string(),
            String::from_utf8_lossy(&stderr_bytes).to_string(),
            stdout_len,
            stderr_len,
        )

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Only write stdin to background tasks you spawned with background: true and stdin capture.
  2. Do not reuse a task after sending close: true — start a new task instead.
  3. Check the task status first; completed/exited tasks have no stdin.
  4. Send all input before sending EOF.
Defensive patterns

Strategy: validation

Validate before calling

// Before writing: task must be running with a live stdin.
let task = shell_manager.get_task_status(&task_id)?;
anyhow::ensure!(
    task.running && task.stdin_open,
    "task {task_id} has no live stdin; spawn with background:true instead"
);
shell_manager.write_stdin(&task_id, input, close)?;

Try / catch

match shell.write_stdin(task_id, input, close) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("stdin is not available") => {
        // stdin is gone (closed earlier or task exited): start a fresh task
        let new_id = spawn_background_with_stdin(command, pending_input)?;
        new_id
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling send_stdin/write_stdin on a task whose stdin was previously closed (an earlier write with close: true), on a task run in foreground/sync mode (no persistent stdin handle), or on a process that already exited and dropped the pipe.

Common situations: An agent sends EOF (close) then tries to send more input; writing to stdin of an already-completed background task; assuming sync exec tasks keep a writable stdin.

Related errors


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