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
- Only write stdin to background tasks you spawned with background: true and stdin capture.
- Do not reuse a task after sending close: true — start a new task instead.
- Check the task status first; completed/exited tasks have no stdin.
- 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
- Send all input (including any EOF) in one write_stdin call or a strict sequence ending with close.
- Never address a task's stdin after sending close: true — start a new task instead.
- Poll task status before writing; exited tasks have no stdin.
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
- Failed to capture stdout
- Failed to capture stderr
- API key input is unexpectedly large
- interactive key entry requires a terminal; use `--api-key-st
- No API key provided. Pass --api-key or pipe one via stdin.
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/cb857acf9ce39536.
Report an issue: GitHub.