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

  1. Spawn the child process with stdout set to piped before reading it.
  2. 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)