{"record":{"id":"cb857acf9ce39536","repo":"Hmbown/CodeWhale","slug":"stdin-is-not-available-for-task","errorCode":null,"errorMessage":"stdin is not available for task {}","messagePattern":"stdin is not available for task (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":1263,"sourceCode":"    fn write_stdin(&mut self, input: &str, close: bool) -> Result<()> {\n        if let Some(stdin) = self.stdin.as_mut() {\n            if !input.is_empty() {\n                stdin\n                    .write_all(input.as_bytes())\n                    .context(\"Failed to write to stdin\")?;\n                stdin.flush().ok();\n            }\n            if close {\n                self.stdin = None;\n            }\n            return Ok(());\n        }\n\n        if input.is_empty() && close {\n            return Ok(());\n        }\n\n        Err(anyhow!(\"stdin is not available for task {}\", self.id))\n    }\n\n    fn full_output(&self) -> (String, String, usize, usize) {\n        if let Some(snapshot) = self.bounded_output_snapshot(false).ok().flatten() {\n            return (snapshot.content, String::new(), snapshot.total_bytes, 0);\n        }\n        let (stdout_bytes, stderr_bytes, stdout_omitted, stderr_omitted) =\n            self.retained_output_bytes_with_omissions();\n        // Report what the stream produced, not what is still held.\n        let stdout_len = stdout_bytes.len().saturating_add(stdout_omitted);\n        let stderr_len = stderr_bytes.len().saturating_add(stderr_omitted);\n\n        (\n            String::from_utf8_lossy(&stdout_bytes).to_string(),\n            String::from_utf8_lossy(&stderr_bytes).to_string(),\n            stdout_len,\n            stderr_len,\n        )","sourceCodeStart":1245,"sourceCodeEnd":1281,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/shell.rs#L1245-L1281","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before writing: task must be running with a live stdin.\nlet task = shell_manager.get_task_status(&task_id)?;\nanyhow::ensure!(\n    task.running && task.stdin_open,\n    \"task {task_id} has no live stdin; spawn with background:true instead\"\n);\nshell_manager.write_stdin(&task_id, input, close)?;","typeGuard":null,"tryCatchPattern":"match shell.write_stdin(task_id, input, close) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"stdin is not available\") => {\n        // stdin is gone (closed earlier or task exited): start a fresh task\n        let new_id = spawn_background_with_stdin(command, pending_input)?;\n        new_id\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["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."],"tags":["shell","stdin","process-io","background-task"],"backgroundTag":"stdin-not-available","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}