affaan-m/ECC · error · anyhow::Error
Child stdout was not piped
Error message
Child stdout was not piped
What it means
capture_command_output requires the spawned child's stdout to be piped (Stdio::piped) so output can be persisted; child.stdout.take() returned None despite the spawn call. This is an invariant violation indicating the Command was constructed without piped stdout; the child is killed and reaped before bailing.
Solutions
- Spawn the child process with stdout set to piped before reading it.
- Ensure no earlier code consumed or detached the stdout handle.
Defensive patterns
Strategy: type-guard
When it happens
Trigger: Thrown at ecc2/src/session/runtime.rs:158 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/0fde28e0c35f8d4b.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/session/runtime.rs:158
mut command: Command,
output_store: SessionOutputStore,
heartbeat_interval: std::time::Duration,
) -> Result<ExitStatus> {
let db_writer = DbWriter::start(db_path, session_id.clone());
let result = async {
let mut child = command
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.with_context(|| format!("Failed to start process for session {}", session_id))?;
let stdout = match child.stdout.take() {
Some(stdout) => stdout,
None => {
let _ = child.kill().await;
let _ = child.wait().await;
anyhow::bail!("Child stdout was not piped");
}
};
let stderr = match child.stderr.take() {
Some(stderr) => stderr,
None => {
let _ = child.kill().await;
let _ = child.wait().await;
anyhow::bail!("Child stderr was not piped");
}
};
let pid = child
.id()
.ok_or_else(|| anyhow::anyhow!("Spawned process did not expose a process id"))?;
db_writer.update_pid(Some(pid)).await?;
db_writer.update_state(SessionState::Running).await?;
db_writer.touch_heartbeat().await?;
View on GitHub (pinned to 06c5e118c4)