#19495: fix: handle Chrome process re-parenting on Windows
size: S
Cluster:
Browser Enhancements and Fixes
## 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
#13568: Fix browser (OpenClaw-managed) launch fail by binding remote debugg...
by singlag · 2026-02-10
81.4%
#8614: fix(browser): detect early chromium exit to prevent startup hang
by Wren-OC · 2026-02-04
79.8%
#14944: fix(browser): prefer openclaw profile in headless/noSandbox environ...
by BenediktSchackenberg · 2026-02-12
79.4%
#9020: fix(browser): skip port ownership and WS reachability checks for re...
by yepack · 2026-02-04
78.0%
#17862: fix: Windows exec returns empty output when pty=false
by MisterGuy420 · 2026-02-16
78.0%
#21459: fix(gateway): resolve port from profile config, not inherited env
by kkeeling · 2026-02-19
77.2%
#22997: fix(browser): remove stale Chrome SingletonLock before launch
by CryptoYogiLLC · 2026-02-21
76.3%
#15595: fix(browser): avoid local port conflicts for remote cdp
by TsekaLuk · 2026-02-13
76.2%
#5496: Fix: Windows path separators stripped in Gateway scheduled task
by giuliozelante · 2026-01-31
75.5%
#10367: CLI/Ops: resilient browser fill + failover hardening + operations t...
by cluster2600 · 2026-02-06
75.4%