affaan-m/ECC · error

Claude Code did not expose a process id

Error message

Claude Code did not expose a process id

What it means

Same family as the runner-pid error but for the Claude Code agent process: child.id() returned None after spawning the agent binary. The manager needs the pid to stop/track the session, so None is converted into an anyhow error.

Solutions

  1. Verify agent_program exists and is executable before spawn
  2. Run the agent manually with the same args to see why it exits instantly
  3. Capture the spawn exit status and surface it instead of only the missing pid

Example fix

// before
child.id().ok_or_else(|| anyhow::anyhow!("Claude Code did not expose a process id"))
// after
let pid = child.id().with_context(|| format!("Claude Code exited immediately: {:?}", child.try_wait()))?;
Defensive patterns

Strategy: try-catch

Validate before calling

if !agent_program.exists() {
    anyhow::bail!("agent binary not found: {}", agent_program.display());
}

Try / catch

match spawn_agent().await {
    Ok(pid) => /* track session */,
    Err(e) if e.to_string().contains("did not expose a process id") => {
        eprintln!("Claude Code exited immediately; run it manually to see the error");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Spawning the Claude Code agent executable where the Child handle's id() is None — the process already exited, or platform-specific pid exposure failed (notably Windows).

Common situations: agent_program path pointing at a wrapper that exits immediately; missing agent binary permissions causing instant death; Windows handle/AV interference.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/995fc3631260694d. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/session/manager.rs:3570

        task,
        session_id,
        working_dir,
        None,
    );
    let child = command
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .with_context(|| {
            format!(
                "Failed to spawn Claude Code from {}",
                agent_program.display()
            )
        })?;

    child
        .id()
        .ok_or_else(|| anyhow::anyhow!("Claude Code did not expose a process id"))
}

async fn stop_session_with_options(
    db: &StateStore,
    id: &str,
    cleanup_worktree: bool,
) -> Result<()> {
    let session = resolve_session(db, id)?;
    stop_session_recorded(db, &session, cleanup_worktree)
}

fn stop_session_recorded(db: &StateStore, session: &Session, cleanup_worktree: bool) -> Result<()> {
    if let Some(pid) = session.pid {
        kill_process(pid)?;
    }

    db.update_pid(&session.id, None)?;
    db.update_state(&session.id, &SessionState::Stopped)?;

View on GitHub (pinned to 8321021c54)