Skip to content

Skip tabs whose renderer crashed when binding Playwright execution - #465

Draft
robertjamesprior wants to merge 1 commit into
mainfrom
hypeship/fix-crashed-tab-binding
Draft

robertjamesprior wants to merge 1 commit into
mainfrom
hypeship/fix-crashed-tab-binding

Conversation

@robertjamesprior

Copy link
Copy Markdown
Contributor

Summary

A tab whose renderer is killed by the guest OOM killer keeps its target and still reports isClosed() === false. Page selection only filtered on isClosed(), so the default (pinned) Playwright executor kept binding page to the dead tab and every call failed with Page crashed for the rest of the session. The browser itself is fine: a live tab clears it.

The fix tracks crashed pages and excludes them from selection:

  • openPages starts a crash listener on every page it enumerates, tracked in a WeakSet.
  • The default executor path skips a crashed active page, and skips crashed candidates in the fallback, so it opens a replacement page instead of binding the corpse.
  • A named executor whose bound tab is crashed now throws TabMissingError, which is the signal the API already turns into "open a replacement tab and resend the call". No Go change is needed for this: the recovery path at playwright_executors.go:281 already exists.

page.on('crash') fires for a kernel-killed renderer — verified on a live 1GiB headless browser while the guest kernel's oom_kill counter went 0 -> 1, with the tab still reporting isClosed() === false afterwards.

Tests

Added a crashed tab is not bound and a replacement page is opened, using the existing loader-stub harness. It fails on main with "bound the crashed tab instead of a replacement" and passes with the fix. node --experimental-vm-modules --test runtime/*.test.ts is 44/44 green and tsc --noEmit is clean.

Known limitations

  • Crash detection covers pages the daemon has already enumerated. A tab that crashed before the daemon first saw it has no event to catch, so the first call to land on it still fails; subsequent calls are unaffected. Closing that gap needs a liveness probe on the selection path, which adds a round trip to every call.
  • The dead tab is left open. Closing it would clear the operator's live view too, but it mutates the customer's tab set, so I left that out. Reload does not revive a crashed tab, so the tab has no value either way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant