{"record":{"id":"f0b8cdcdf6576eba","repo":"affaan-m/ECC","slug":"ecc-runner-did-not-expose-a-process-id","errorCode":null,"errorMessage":"ECC runner did not expose a process id","messagePattern":"ECC runner did not expose a process id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/session/manager.rs","lineNumber":3027,"sourceCode":"        .arg(session_id)\n        .arg(\"--task\")\n        .arg(task)\n        .arg(\"--agent\")\n        .arg(agent_type)\n        .arg(\"--cwd\")\n        .arg(working_dir)\n        .stdin(Stdio::null())\n        .stdout(Stdio::null())\n        .stderr(Stdio::from(stderr_log));\n    configure_background_runner_command(&mut command);\n\n    let child = command\n        .spawn()\n        .with_context(|| format!(\"Failed to spawn ECC runner from {}\", current_exe.display()))?;\n\n    child\n        .id()\n        .ok_or_else(|| anyhow::anyhow!(\"ECC runner did not expose a process id\"))?;\n    Ok(())\n}\n\nfn background_runner_stderr_log_path(working_dir: &Path, session_id: &str) -> PathBuf {\n    working_dir\n        .join(\".claude\")\n        .join(\"ecc2\")\n        .join(\"logs\")\n        .join(format!(\"{session_id}.runner-stderr.log\"))\n}\n\n#[cfg(windows)]\nfn detached_creation_flags() -> u32 {\n    const DETACHED_PROCESS: u32 = 0x0000_0008;\n    const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;\n    DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP\n}\n","sourceCodeStart":3009,"sourceCodeEnd":3045,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/manager.rs#L3009-L3045","documentation":"After spawning the ECC runner child process, the code reads child.id(); on some platforms/spawn failure modes std returns None even though spawn() reported success. The manager treats an unknown pid as fatal because session stop/monitoring depends on it.","triggerScenarios":"command.spawn() succeeds but the Child handle reports no pid — typically on Windows where pid exposure can fail, or when the process exits/reaps extremely quickly between spawn and id().","commonSituations":"Windows targets with restricted process handles; antivirus/EDR intercepting the spawned binary; spawning a binary path that dies instantly.","solutions":["Retry the spawn once; transient races usually resolve","Check that the runner executable at current_exe-relative path exists and is not blocked by AV/EDR","Log the OS-level spawn error and fall back to running the runner synchronously if pid tracking is not required"],"exampleFix":"// before\nchild.id().ok_or_else(|| anyhow::anyhow!(\"ECC runner did not expose a process id\"))?;\n// after\nlet pid = child.id().with_context(|| format!(\"ECC runner exited before pid could be read (status: {:?})\", child.try_wait()))?;","handlingStrategy":"try-catch","validationCode":"// no pre-call check possible for child.id(); verify executable first\nassert!(current_exe.join(\"runner\").exists(), \"runner binary missing\");","typeGuard":null,"tryCatchPattern":"match spawn_runner().await {\n    Ok(pid) => track(pid),\n    Err(e) if e.to_string().contains(\"did not expose a process id\") => {\n        eprintln!(\"runner exited before pid capture; check runner logs\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Ensure the runner binary is present and not blocked by AV/EDR","Test spawns on the target OS (Windows quirks)","Detect instant child exit via try_wait and report its status"],"tags":["rust","process","spawn","pid"],"backgroundTag":"process-pid-unavailable","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}