← Back to PRs

#19495: fix: handle Chrome process re-parenting on Windows

by andenwick open 2026-02-17 21:42 View on GitHub →
size: S
## Summary On Windows, Chrome's spawned parent process exits quickly as it re-parents to child/broker processes. OpenClaw's `proc.on("exit")` handler clears `profileState.running`, causing `browser stop`, `restart`, and `reset-profile` to fail. The gateway loses tracking of Chrome, leaving orphaned processes and occupied ports. Fixes #14215 ## Changes - **Deferred exit handler** (`server-context.ts`): On Windows, defer the `setProfileRunning(null)` call by 1500ms and check if CDP is still reachable before clearing state. If Chrome re-parented and is still alive, keep tracking it. Non-Windows behavior is unchanged. - **Port-based stop fallback** (`chrome.ts`): When `proc.kill()` targets a dead parent and Chrome is still reachable, find the actual owning PID via `netstat -ano` and kill it with `taskkill /F /T /PID`. This ensures `browser stop` actually kills re-parented Chrome. - **Reset-profile orphan cleanup** (`server-context.ts`): When `resetProfile` finds the port in use but `profileState.running` is null (orphaned Chrome from re-parenting), kill the process by port before attempting to delete the profile directory. - **New utility** (`chrome.ts`): `findListeningPid(port)` finds the PID listening on a port via `netstat`. Returns null on non-Windows. ## Testing - All 18 existing + new tests pass (`vitest run src/browser/chrome.test.ts`) - New tests cover: fallback when Chrome survives SIGTERM, no-hang guarantee, `findListeningPid` platform guards - `findListeningPid` and `taskkill /F /T /PID` verified live on Windows 10 against real Chrome - Full `pnpm build` succeeds with no errors ## AI Disclosure - [x] AI-assisted (OpenClaw + Claude Opus) - [x] Lightly tested (unit tests + manual component verification on Windows; no full e2e gateway integration test) - [x] I understand what the code does <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes a real Windows-specific bug where Chrome's process re-parenting caused OpenClaw to lose track of running browser instances, leaving orphaned processes and occupied ports. The fix is applied in three places: a 1500ms deferred reachability check before clearing `profileState.running` on Windows, a port-based `taskkill` fallback in `stopOpenClawChrome` when the original proc is a dead parent, and a `taskkill` cleanup in `resetProfile` for orphaned Chrome detected during profile reset. The core logic is sound and the approach is well-targeted at the Windows re-parenting scenario. Key findings from the review: - **Misleading test with dead code** (`chrome.test.ts` lines 284–311): The test "stopOpenClawChrome falls back to findListeningPid when Chrome survives SIGTERM" declares `realExecSync` and `mockExecSync` but immediately abandons them (per the inline comment) and instead tests the Linux SIGKILL path. The unused variable declarations are dead code that will likely trigger oxlint `correctness` errors, and the test name is inaccurate. - **Redundant guard** (`server-context.ts` line 530): `profile.cdpIsLoopback &&` is always `true` at that point since line 512 already throws for non-loopback profiles. - The `findListeningPid` function is declared `async` but only calls `execSync` (synchronous) — minor style inconsistency, no functional impact. <h3>Confidence Score: 3/5</h3> - The core Windows re-parenting fix is logically sound, but the new test for the primary fallback path contains dead code and does not actually exercise the Windows `taskkill` code path it claims to test. - The deferred exit handler and port-based kill fallback are architecturally correct and have appropriate guards. The main concern is test quality: the key new test for the Windows fallback path is effectively a renamed Linux SIGKILL test with dead variable declarations, which will likely fail the oxlint `correctness: error` check. The fix itself has been manually verified on Windows per the PR description. - src/browser/chrome.test.ts — the "falls back to findListeningPid" test contains unused variables and tests the wrong platform path. <sub>Last reviewed commit: 4133c32</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs