{"record":{"id":"506af55eb64245b7","repo":"BigPizzaV3/CodexPlusPlus","slug":"previous-launcher-has-not-exited-no-process-was-forcibly","errorCode":null,"errorMessage":"Previous launcher has not exited; no process was forcibly terminated","messagePattern":"Previous launcher has not exited; no process was forcibly terminated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/watcher.rs","lineNumber":584,"sourceCode":"                captured.push((pid, birth));\n            } else {\n                anyhow::bail!(\"Cannot verify launcher process identity: {pid}\");\n            }\n        }\n        Ok(Self { processes: captured })\n    }\n\n    pub fn wait_for_exit(self, timeout: Duration) -> anyhow::Result<()> {\n        let deadline = std::time::Instant::now() + timeout;\n        loop {\n            let remaining = launcher_incarnations_still_running(\n                &self.processes,\n                |pid| crate::windows_integration::process_birth_id(pid),\n            );\n            if remaining.is_empty() {\n                return Ok(());\n            }\n            anyhow::ensure!(\n                std::time::Instant::now() < deadline,\n                \"Previous launcher has not exited; no process was forcibly terminated\"\n            );\n            std::thread::sleep(Duration::from_millis(RESTART_STOP_WAIT_INTERVAL_MS));\n        }\n    }\n}\n\n#[cfg(any(windows, test))]\nfn launcher_incarnations_still_running(\n    captured: &[(u32, u64)],\n    mut birth_id: impl FnMut(u32) -> Option<u64>,\n) -> Vec<u32> {\n    captured.iter().filter_map(|(pid, birth)| {\n        (birth_id(*pid) == Some(*birth)).then_some(*pid)\n    }).collect()\n}\n","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/watcher.rs#L566-L602","documentation":"During a launcher restart on Windows, the manager captures a snapshot of the previous launcher process incarnations (pid + birth id) and waits for them to exit. wait_for_exit polls until the deadline; if the processes are still alive (matched by birth id so PID reuse does not fool it) when the timeout expires, it fails. The message also clarifies that no forceful termination was performed by this wait path — it only observed non-exit.","triggerScenarios":"Calling LauncherExitSnapshot::wait_for_exit(timeout) after stop_launcher_processes_and_wait when the previous launcher process does not terminate within RESTART_STOP_WAIT_TIMEOUT_MS; each poll finds launcher_incarnations_still_running non-empty until Instant::now() >= deadline.","commonSituations":"The old launcher process is hung, blocked on an unsaved dialog, stuck in a blocking I/O call, or antivirus/software is preventing termination; very short timeout configured on a slow machine; the killable-filter skipped the actual parent process so it was never asked to exit.","solutions":["Increase the wait timeout passed to wait_for_exit (or RESTART_STOP_WAIT_TIMEOUT_MS) and retry the restart","Manually kill the stale launcher process (Task Manager / taskkill /PID <pid>) and retry","Check whether the process is blocked on a modal dialog or file lock; close it and rerun","Verify the process is actually the previous incarnation (birth id check) and not an unrelated process that happens to match the killable filter"],"exampleFix":"// before\nsnapshot.wait_for_exit(Duration::from_secs(2))?;\n// after\nsnapshot.wait_for_exit(Duration::from_secs(30))?;","handlingStrategy":"try-catch","validationCode":"// before restarting, check whether a stale launcher is still alive\nlet snapshot = LauncherExitSnapshot::capture()?;\nif !snapshot.wait_for_exit(Duration::from_secs(0)).is_ok() {\n    eprintln!(\"stale launcher still running; kill it manually before restart\");\n}","typeGuard":null,"tryCatchPattern":"match snapshot.wait_for_exit(Duration::from_secs(30)) {\n    Ok(()) => proceed_with_restart(),\n    Err(e) if e.to_string().contains(\"not exited\") => {\n        // prompt user to close/kill the stale launcher, then retry once\n        prompt_and_retry_with_longer_timeout();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Use a generous timeout (>=30s) for wait_for_exit on slow machines","Ensure stop_launcher_processes_and_wait actually terminated the filtered processes before waiting","Detect hung launchers early (UI indicating an old instance is running)","Log captured pids + birth ids so stale processes can be identified and killed manually"],"tags":["windows","process-management","timeout","launcher-restart"],"backgroundTag":"request-timeout","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}