BigPizzaV3/CodexPlusPlus · error

Cannot verify launcher process identity

Error message

Cannot verify launcher process identity: {pid}

What it means

capture() snapshots launcher process identity by reading each PID's birth id via the Windows integration layer. If process_birth_id returns None for any PID, the process identity cannot be verified and capture aborts with this error so the watcher never waits on the wrong process.

Solutions

  1. Verify the launcher is still running right before calling capture and pass the pid of a live process
  2. Re-scan the process list to obtain a fresh pid and retry capture
  3. Treat the error as transient: retry capture shortly after spawn, or skip waiting for the dead launcher and report it as exited

Example fix

// before
let snapshot = LauncherSnapshot::capture(&[stale_pid], ...)?;
// after
if crate::windows_integration::process_birth_id(pid).is_some() {
    let snapshot = LauncherSnapshot::capture(&[pid], ...)?;
} else {
    // respawn launcher and recapture with fresh pid
}
Defensive patterns

Strategy: retry

Validate before calling

fn pid_alive(pid: u32) -> bool {
    crate::windows_integration::process_birth_id(pid).is_some()
}

Type guard

fn verified_pid(pid: u32) -> Option<u32> {
    crate::windows_integration::process_birth_id(pid).map(|_| pid)
}

Try / catch

match LauncherSnapshot::capture(&[pid], ...) {
    Err(e) if e.to_string().contains("Cannot verify launcher process identity") => {
        // process exited before snapshot; rescan and retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling capture with a PID that has already exited, or a PID for which the OS denies/omits creation-time info (recycled PID, protected process, invalid pid).

Common situations: Launcher crashed before the watcher could snapshot it; PID reuse gave a stale id; spawning raced with process exit; passing an incorrect pid parsed from stale state.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/64f75ed90d3757f4. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/watcher.rs:568

#[cfg(windows)]
pub struct LauncherExitSnapshot {
    processes: Vec<(u32, u64)>,
}

#[cfg(windows)]
impl LauncherExitSnapshot {
    pub fn capture() -> anyhow::Result<Self> {
        let processes = crate::windows_integration::enumerate_processes();
        let ids = filter_killable_launcher_processes(
            processes.iter().map(|p| (p.process_id, p.parent_process_id, p.exe_file.as_str())),
            std::process::id(),
        );
        let mut captured = Vec::new();
        for pid in ids {
            if let Some(birth) = crate::windows_integration::process_birth_id(pid) {
                captured.push((pid, birth));
            } else {
                anyhow::bail!("Cannot verify launcher process identity: {pid}");
            }
        }
        Ok(Self { processes: captured })
    }

    pub fn wait_for_exit(self, timeout: Duration) -> anyhow::Result<()> {
        let deadline = std::time::Instant::now() + timeout;
        loop {
            let remaining = launcher_incarnations_still_running(
                &self.processes,
                |pid| crate::windows_integration::process_birth_id(pid),
            );
            if remaining.is_empty() {
                return Ok(());
            }
            anyhow::ensure!(
                std::time::Instant::now() < deadline,
                "Previous launcher has not exited; no process was forcibly terminated"

View on GitHub (pinned to b1ed92e5e4)