mirror of https://github.com/garrytan/gstack.git
`browser?.process()` in headed mode reaches a BrowserContext-owned Browser
stub whose `.process` is undefined (not a function). The optional-chain
`browser?.process()` does NOT short-circuit on undefined methods — only
on null/undefined receivers — so it evaluates to `undefined()` and throws
an unhandled rejection. The throw crashes the bun process, gbd respawns
it, the next tab close hits the same path, loop forever.
Reproducer (live in gbrowser amsterdam-v7 right now):
[overlay] Local listener bound on 127.0.0.1:35300 (PID: 19445)
[browse] Tab closed (id=1, remaining=0)
[stderr] [overlay] FATAL unhandled rejection: browser?.process is
not a function. (In 'browser?.process()', 'browser?.process'
is undefined)
[browse] Shutting down...
...respawn, same crash, repeat...
Fix: split the null case from the no-process case.
- null browser → 'crash' (preserves the existing contract pinned by the
"null browser returns crash" test)
- truthy browser without callable .process → 'clean' (persistent contexts
in headed mode; the user controls the lifecycle so the right default
is exit 0 / gbd does not restart)
- truthy browser with callable .process → unchanged exit-code introspection
In headed mode we genuinely cannot distinguish "user pressed Cmd+Q or
closed all tabs" from "Chromium crashed" because Playwright doesn't
expose the underlying Chromium PID through a persistent context. The
tradeoff is: if Chromium genuinely crashes in headed mode we now exit 0
and don't auto-restart. That's preferable to the respawn loop, and the
user can re-launch manually if they want.
Test: added "clean: browser without .process method (persistent context)"
which would have caught this bug. All 21 browser-manager-unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| bin | ||
| scripts | ||
| src | ||
| test | ||
| PLAN-snapshot-dropdown-interactive.md | ||
| SKILL.md | ||
| SKILL.md.tmpl | ||