Skip to content

fix(browser): guard Playwright pageError undefined-location crash (CC-161) - #56

Merged
overcast-software merged 1 commit into
mainfrom
fix/cc161-playwright-pageerror-crash
Jul 13, 2026
Merged

overcast-software merged 1 commit into
mainfrom
fix/cc161-playwright-pageerror-crash

Conversation

@voodoologic

Copy link
Copy Markdown
Collaborator

CC-161 — the disease behind CC-141 / CC-160 driver deaths

Playwright 1.60's vendored Node driver (coreBundle.js) reads pageError.location.url unconditionally. Firefox/Camoufox can emit a page error with no location (cross-origin "Script error.", CSP, some uncaught errors). LinkedIn triggers it deterministically; jobright.ai never does. The unguarded read throws a synchronous TypeError: Cannot read properties of undefined (reading 'url') in the Node event loop → the whole driver process exits → every subsequent call returns "Connection closed while reading from the driver" (the CC-141/160 "driver death"). CC-141/160 made the crash survivable but a relaunched LinkedIn scrape re-crashes on the same pageError, so LinkedIn never completes headed. This stops the crash.

Approach: A (surgical local patch) — verified, not assumed

Option B (Playwright bump) was verified infeasible: I installed and inspected the bundles — both 1.60.0 AND 1.61.0 still ship the unguarded read (the upstream guard, microsoft/playwright#41629, is unreleased). camoufox's own juggler-side fix (daijro/camoufox#625) is unreleased on PyPI (latest camoufox is 0.4.11, our pinned version, which lacks it). So a version bump does not fix the crash today, and Option A is the only fix that keeps Camoufox stealth.

  • scripts/patch_playwright_pageerror.py — guards the two vendored crash sites (the PageError dispatcher + the _onPageError trace-event writer) with pageError.location?.url ?? null (+ line/column). Idempotent, shape-matched (fails loudly if upstream shape changes), repeatable after every uv sync / reinstall. Normal pageerror behaviour preserved when location is present (optional chaining short-circuits only on nullish).
  • Dockerfile — runs the patch after the final uv sync so the built agents image carries the guard.
  • tests/test_playwright_pageerror_patch.py — 6 tests: shape match, both crash sites, idempotency, loud-fail on unknown shape, and an assertion that the installed bundle carries the guard.

Local-dev wiring note

Local dev must run uv run python scripts/patch_playwright_pageerror.py after uv sync (and after python -m camoufox fetch reinstalls). The Dockerfile hook is in this repo (agents-owned). The parent's make/compose that drive local uv sync may want the same post-sync call — flagging for cc-orchestrator to decide if a parent-side Makefile hook is warranted (this PR does not touch parent).

Test results

  • New: tests/test_playwright_pageerror_patch.py — 6 passed.
  • Full agents suite: 503 passed.
  • ruff: clean on all changed files. (Pre-existing tools/discover_sites.py syntax errors are unrelated and untouched.)

Build-don't-ship; no parent pin bump (Doug-gated). No graph structure change → no graph_static.json regen.

🤖 Generated with Claude Code

…-161)

Playwright 1.60's vendored Node driver reads pageError.location.url
unconditionally. Firefox/Camoufox can emit a page error with no location
(cross-origin "Script error.", CSP, uncaught errors) -- LinkedIn does this --
and the unguarded read throws a synchronous TypeError that kills the whole
driver process. Every subsequent call then returns "Connection closed while
reading from the driver": the true root cause behind the CC-141/CC-160 driver
deaths. A relaunched LinkedIn scrape re-crashes on the same pageError, so those
tickets' survive-and-relaunch loop can never complete a LinkedIn scrape.

Fix: scripts/patch_playwright_pageerror.py surgically guards the two vendored
crash sites (dispatcher + trace-event writer) with optional chaining and a null
default, preserving normal pageerror behaviour when location is present. The
patch is idempotent, shape-matched (fails loudly if upstream code changes), and
repeatable after every uv sync / reinstall. The Dockerfile runs it after the
final uv sync so the built agents image carries the guard.

Verified the upstream guard (microsoft/playwright#41629) is unreleased: both
1.60.0 and 1.61.0 still ship the unguarded read. camoufox's juggler-side fix
(daijro/camoufox#625) is likewise unreleased on PyPI (latest 0.4.11). So the
local patch is the only fix that keeps Camoufox stealth today -- a version bump
does not resolve it.

Tests: tests/test_playwright_pageerror_patch.py (6) -- shape match, both crash
sites, idempotency, loud-fail on unknown shape, and the installed bundle
carries the guard. Full agents suite 503 passed, ruff clean on changed files.
@overcast-software
overcast-software merged commit 7a82874 into main Jul 13, 2026
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.

2 participants