{"record":{"id":"f53572ba51ed5620","repo":"BigPizzaV3/CodexPlusPlus","slug":"failed-to-wait-for-windows-process-id-process-id","errorCode":null,"errorMessage":"failed to wait for Windows process id {process_id}","messagePattern":"failed to wait for Windows process id (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/codex-plus-core/src/launcher.rs","lineNumber":2981,"sourceCode":"#[cfg(windows)]\nfn wait_for_windows_process_id_blocking(process_id: u32) -> anyhow::Result<()> {\n    use windows::Win32::Foundation::{CloseHandle, WAIT_FAILED};\n    use windows::Win32::System::Threading::{\n        INFINITE, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE,\n        WaitForSingleObject,\n    };\n\n    unsafe {\n        let handle = OpenProcess(\n            PROCESS_SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,\n            false,\n            process_id,\n        )\n        .with_context(|| format!(\"failed to open Windows process id {process_id}\"))?;\n        let wait_result = WaitForSingleObject(handle, INFINITE);\n        let _ = CloseHandle(handle);\n        if wait_result == WAIT_FAILED {\n            anyhow::bail!(\"failed to wait for Windows process id {process_id}\");\n        }\n    }\n    Ok(())\n}\n\n#[cfg(windows)]\nfn terminate_windows_process_id_blocking(process_id: u32) -> anyhow::Result<()> {\n    use windows::Win32::Foundation::CloseHandle;\n    use windows::Win32::System::Threading::{\n        OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_TERMINATE, TerminateProcess,\n    };\n\n    unsafe {\n        let handle = OpenProcess(\n            PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION,\n            false,\n            process_id,\n        )","sourceCodeStart":2963,"sourceCodeEnd":2999,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/launcher.rs#L2963-L2999","documentation":"wait_for_windows_process_id_blocking (crates/codex-plus-core/src/launcher.rs:2964, Windows-only) opens the child process with PROCESS_SYNCHRONIZE and blocks in WaitForSingleObject(handle, INFINITE) to observe exit. If the wait returns WAIT_FAILED — after OpenProcess itself succeeded — it bails with this message. WAIT_FAILED means the wait itself errored (invalid handle state, waiting on an object that cannot be waited on, or the handle was invalidated), which is distinct from a timeout (impossible with INFINITE) or a clean signal.","triggerScenarios":"Launch flow on Windows where the spawned Codex process handle fails WaitForSingleObject: process terminated and handle already signalled-invalidated in edge cases, handle closed by antivirus/injection tooling, or the pid reused pointing at a non-waitable system pseudo-process.","commonSituations":"Security software tampering with child process handles; spawning then immediately killing the process so the wait hits a dead handle race; passing a pid of a system process or a stale pid after restart; heavy load causing the child to die between spawn and wait.","solutions":["Treat as best-effort: the caller only waits to observe exit — catch the error, verify liveness with a fallback (process enumeration or polling) instead of failing the launch","Check whether the child actually exited (exit code via GetExitCodeProcess) — often the process is already gone and the wait is moot","Reproduce with the pid: OpenProcess(PROCESS_SYNCHRONIZE, ...) + WaitForSingleObject in a scratch program to see whether AV or handle inheritance is interfering","If persistent in your environment, fork to use RegisterWaitForSingleObject or a job object (AssignProcessToJobObject) for robust child tracking"],"exampleFix":"// before: any wait failure aborts the caller\nwait_for_windows_process_id(pid).await?;\n\n// after: wait is advisory — degrade to a liveness check\nif let Err(error) = wait_for_windows_process_id(pid).await {\n    tracing::warn!(%pid, %error, \"process wait failed; falling back to liveness polling\");\n    while process_still_alive(pid) {\n        tokio::time::sleep(Duration::from_millis(250)).await;\n    }\n}","handlingStrategy":"fallback","validationCode":"#[cfg(windows)]\nfn can_open_for_wait(process_id: u32) -> bool {\n    use windows::Win32::System::Threading::{OpenProcess, PROCESS_SYNCHRONIZE};\n    unsafe { OpenProcess(PROCESS_SYNCHRONIZE, false, process_id).is_ok() }\n}","typeGuard":null,"tryCatchPattern":"if let Err(error) = wait_for_windows_process_id(pid).await {\n    tracing::warn!(pid, %error, \"wait failed; falling back to liveness polling\");\n    while process_alive(pid) { tokio::time::sleep(Duration::from_millis(250)).await; }\n}","preventionTips":["Treat child-exit waits as advisory: pair with a liveness-polling fallback","Use job objects (AssignProcessToJobObject) for robust child lifetime tracking on Windows","Watch for AV/EDR software interfering with inherited child handles"],"tags":["rust","windows","win32-api","process-management","waitfor-singleobject"],"backgroundTag":"win32-api-call-failed","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}