BigPizzaV3/CodexPlusPlus · warning · anyhow::Error
failed to wait for Windows process id {process_id}
Error message
failed to wait for Windows process id {process_id} What it means
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.
Source
Thrown at crates/codex-plus-core/src/launcher.rs:2981
#[cfg(windows)]
fn wait_for_windows_process_id_blocking(process_id: u32) -> anyhow::Result<()> {
use windows::Win32::Foundation::{CloseHandle, WAIT_FAILED};
use windows::Win32::System::Threading::{
INFINITE, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE,
WaitForSingleObject,
};
unsafe {
let handle = OpenProcess(
PROCESS_SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,
false,
process_id,
)
.with_context(|| format!("failed to open Windows process id {process_id}"))?;
let wait_result = WaitForSingleObject(handle, INFINITE);
let _ = CloseHandle(handle);
if wait_result == WAIT_FAILED {
anyhow::bail!("failed to wait for Windows process id {process_id}");
}
}
Ok(())
}
#[cfg(windows)]
fn terminate_windows_process_id_blocking(process_id: u32) -> anyhow::Result<()> {
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_TERMINATE, TerminateProcess,
};
unsafe {
let handle = OpenProcess(
PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION,
false,
process_id,
)View on GitHub (pinned to 1f431ae49b)
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
Example fix
// before: any wait failure aborts the caller
wait_for_windows_process_id(pid).await?;
// after: wait is advisory — degrade to a liveness check
if let Err(error) = wait_for_windows_process_id(pid).await {
tracing::warn!(%pid, %error, "process wait failed; falling back to liveness polling");
while process_still_alive(pid) {
tokio::time::sleep(Duration::from_millis(250)).await;
}
} Defensive patterns
Strategy: fallback
Validate before calling
#[cfg(windows)]
fn can_open_for_wait(process_id: u32) -> bool {
use windows::Win32::System::Threading::{OpenProcess, PROCESS_SYNCHRONIZE};
unsafe { OpenProcess(PROCESS_SYNCHRONIZE, false, process_id).is_ok() }
} Try / catch
if let Err(error) = wait_for_windows_process_id(pid).await {
tracing::warn!(pid, %error, "wait failed; falling back to liveness polling");
while process_alive(pid) { tokio::time::sleep(Duration::from_millis(250)).await; }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- SetThreadDpiAwarenessContext failed
- invalid runtime file path
- missing cached {plugin} plugin for openai-bundled marketplac
- provider sync requires launcher hooks with codex-plus-data i
- Remote Control session recovery requires launcher hooks with
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/f53572ba51ed5620.
Report an issue: GitHub.