{"record":{"id":"0fde28e0c35f8d4b","repo":"affaan-m/ECC","slug":"child-stdout-was-not-piped","errorCode":null,"errorMessage":"Child stdout was not piped","messagePattern":"Child stdout was not piped","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/session/runtime.rs","lineNumber":158,"sourceCode":"    mut command: Command,\n    output_store: SessionOutputStore,\n    heartbeat_interval: std::time::Duration,\n) -> Result<ExitStatus> {\n    let db_writer = DbWriter::start(db_path, session_id.clone());\n\n    let result = async {\n        let mut child = command\n            .stdout(Stdio::piped())\n            .stderr(Stdio::piped())\n            .spawn()\n            .with_context(|| format!(\"Failed to start process for session {}\", session_id))?;\n\n        let stdout = match child.stdout.take() {\n            Some(stdout) => stdout,\n            None => {\n                let _ = child.kill().await;\n                let _ = child.wait().await;\n                anyhow::bail!(\"Child stdout was not piped\");\n            }\n        };\n        let stderr = match child.stderr.take() {\n            Some(stderr) => stderr,\n            None => {\n                let _ = child.kill().await;\n                let _ = child.wait().await;\n                anyhow::bail!(\"Child stderr was not piped\");\n            }\n        };\n\n        let pid = child\n            .id()\n            .ok_or_else(|| anyhow::anyhow!(\"Spawned process did not expose a process id\"))?;\n        db_writer.update_pid(Some(pid)).await?;\n        db_writer.update_state(SessionState::Running).await?;\n        db_writer.touch_heartbeat().await?;\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/runtime.rs#L140-L176","documentation":"When spawning a child process for a session with Stdio::piped() configured for stdout, the code expects child.stdout to be Some(...). If the platform does not provide a piped handle despite the Stdio::piped() configuration, child.stdout is None and the function bails after killing the child to avoid a zombie process.","triggerScenarios":"Calling the session spawn path where command.stdout(Stdio::piped()).spawn() succeeds but the returned child.stdout is None.","commonSituations":"Platform-specific failure in pipe creation that does not surface as a spawn error. Resource exhaustion (too many open file descriptors preventing pipe allocation). Bug or edge case in the Rust standard library's process spawning on the target platform.","solutions":["Check system file descriptor limits (ulimit -n) and raise if exhausted","Reduce the number of concurrently spawned sessions to avoid pipe resource exhaustion","Verify the platform supports piped stdio for child processes","If this is a transient resource issue, retry the spawn after reducing concurrent sessions"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match spawn_session_process(command, session_id).await {\n    Ok((stdout, stderr, pid)) => Ok((stdout, stderr, pid)),\n    Err(e) if e.to_string().contains(\"not piped\") => {\n        tracing::error!(\"stdio pipe allocation failed for session {session_id}; check fd limits\");\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Monitor system file descriptor limits (ulimit -n) and raise them if sessions fail to spawn","Reduce concurrent session count if pipe resources are exhausted","Implement retry logic for spawn failures that may be transient resource issues"],"tags":["process","spawn","stdio","pipe"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}