{"record":{"id":"b2e3b22b2a682807","repo":"bensadeh/tailspin","slug":"could-not-capture-stdout-of-spawned-process","errorCode":null,"errorMessage":"Could not capture stdout of spawned process","messagePattern":"Could not capture stdout of spawned process","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/io/reader/command.rs","lineNumber":59,"sourceCode":"    }\n}\n\n#[cfg(not(windows))]\nfn spawn_command(command: String) -> Result<CommandReader> {\n    use crate::io::reader::line_batcher::BUF_READER_CAPACITY;\n    use anyhow::Context;\n    use std::process::{Command, Stdio};\n\n    let trap_command = format!(\"trap '' INT; {command}\");\n\n    let mut sh = Command::new(\"sh\");\n    sh.arg(\"-c\").arg(trap_command).stdout(Stdio::piped());\n\n    let child = SharedChild::spawn(&mut sh).context(\"Could not spawn process\")?;\n\n    let stdout = child\n        .take_stdout()\n        .ok_or_else(|| anyhow!(\"Could not capture stdout of spawned process\"))?;\n\n    let reader = BufReader::with_capacity(BUF_READER_CAPACITY, stdout);\n\n    Ok(CommandReader {\n        reader,\n        child: Arc::new(child),\n        initial_read_complete_sent: false,\n    })\n}\n\n#[cfg(windows)]\nfn spawn_command(_command: String) -> Result<CommandReader> {\n    Err(anyhow!(\"The --exec flag is not supported on Windows\"))\n}\n\nimpl Drop for CommandReader {\n    fn drop(&mut self) {\n        let _ = self.child.kill();","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/bensadeh/tailspin/blob/8ecaa9a8a1bf3ba1e7457a20de11d2df75327d81/src/io/reader/command.rs#L41-L77","documentation":"This error is thrown when the spawned child process has no piped stdout to capture. The library sets stdout(Stdio::piped()) before spawning, so take_stdout() returning None indicates the pipe could not be established or was already consumed — without it the CommandReader cannot stream output.","triggerScenarios":"Calling CommandReader::new (via spawn_command) when SharedChild::spawn succeeded but child.take_stdout() returns None. Practically this happens only if the stdout pipe was not set to piped before spawn or was taken elsewhere.","commonSituations":"Code changes that remove or reorder the .stdout(Stdio::piped()) call; spawning a command whose stdout is redirected to a file or /dev/null by an outer wrapper; duplicated take_stdout() calls in modified code.","solutions":["Verify .stdout(Stdio::piped()) is called on the Command before spawning","Ensure take_stdout() is called exactly once and before waiting on the child","Check no wrapper/indirection redirects the child's stdout away from the pipe","If the error persists, log the spawn configuration and upgrade/patch the shared-child usage"],"exampleFix":"// before\nlet mut sh = Command::new(shell); // no stdout configuration\n// after\nsh.arg(\"-c\").arg(trap_command).stdout(Stdio::piped());","handlingStrategy":"validation","validationCode":"// Before constructing the reader, confirm the command will have a piped stdout:\nlet mut sh = Command::new(\"sh\");\nsh.arg(\"-c\").arg(cmd).stdout(Stdio::piped());\nassert!(sh.get_stdout().is_some() || std::env::var(\"CI\").is_ok(),\n        \"stdout must be piped before spawn\");","typeGuard":null,"tryCatchPattern":"let reader = CommandReader::new(cmd).map_err(|e| {\n    if e.to_string().contains(\"Could not capture stdout\") {\n        eprintln!(\"stdout pipe missing; check Stdio config\");\n    }\n    e\n})?;","preventionTips":["Always call .stdout(Stdio::piped()) on the child Command before spawn","Call take_stdout() exactly once, immediately after spawn","Do not redirect the child's stdout to a file or inherit it in wrapper code","Review diffs around spawn configuration when this error appears after a refactor"],"tags":["subprocess","stdout","pipe"],"backgroundTag":"missing-stdout-pipe","analyzedSha":"8ecaa9a8a1bf3ba1e7457a20de11d2df75327d81","analyzedAt":"2026-09-13T19:04:00.202Z","contentChangedAt":"2026-09-13T19:04:00.202Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}