Skip to content

fix(cli): surface HYPERFRAMES_BROWSER_PATH hint on macOS <13 chrome-headless-shell dyld crash#2569

Open
vanceingalls wants to merge 1 commit into
mainfrom
via/macos12-dyld-crash-hint
Open

fix(cli): surface HYPERFRAMES_BROWSER_PATH hint on macOS <13 chrome-headless-shell dyld crash#2569
vanceingalls wants to merge 1 commit into
mainfrom
via/macos12-dyld-crash-hint

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

What

Add a darwin-scoped launch-crash remediation to handleRenderError in packages/cli/src/commands/render.ts. When the pinned chrome-headless-shell binary fails to launch with dyld: Symbol not found: _kVTCompressionPropertyKey_ReferenceBufferCount from VideoToolbox, surface a scoped errorBox that names HYPERFRAMES_BROWSER_PATH (and its PRODUCER_HEADLESS_SHELL_PATH alias — see #2471) with a macOS-specific chrome-headless-shell@150 install command.

Why

Field feedback in #hyperframes-cli-feedback — message ts 1784227832, darwin/x64 macOS 12, HyperFrames CLI 0.7.60 — hit the exact error dyld: Symbol not found: _kVTCompressionPropertyKey_ReferenceBufferCount referenced from VideoToolbox when launching the bundled chrome-headless-shell mac-152.0.7928.2. The symbol was added in macOS 13; older hosts cannot resolve it, so the binary aborts at load before any browser process starts.

The reporter recovered by installing chrome-headless-shell@150 via @puppeteer/browsers and pointing PRODUCER_HEADLESS_SHELL_PATH at it. Their check/snapshot commands accepted the older cached shell (they call ensureBrowser() without preferManagedChrome, so any cached headless-shell is fine), but render requires the pinned v152 via preferManagedChrome: true and therefore couldn't fall back on its own. Their 49s 1080x1920 render then completed cleanly.

The CLI's generic post-render hint (Try --docker etc.) doesn't name any of the browser-path env vars, so the workaround is undiscoverable unaided.

This is the third platform-scoped sibling of #2443 (download-time hint):

Same remediation surface, different trigger and platform.

How

Net diff: +182 / -0 across 3 files (2 new, 1 modified).

Test plan

  • Unit tests added — packages/cli/src/browser/macosOldChromeCrash.test.ts (11 vitest cases):
    • positive match on the exact reporter error text
    • positive match on VideoToolbox framework signal without the exact ReferenceBufferCount symbol name (future pinned builds may hit sibling VT symbols)
    • negative on Linux shared-lib launch failures (those hit chromeLaunchRemediation)
    • negative on darwin Symbol-not-found from unrelated frameworks (e.g. libc++.dylib) — needs different remediation
    • negative on the symbol name alone without the launch-failure wrapper
    • off-platform (non-darwin) short-circuits and non-launch short-circuits
  • bunx vitest run src/browser/macosOldChromeCrash.test.ts11/11 passing, 4ms
  • bunx oxlint packages/cli/src/browser/macosOldChromeCrash{,\.test}.ts packages/cli/src/commands/render.ts — 0 warnings 0 errors
  • bunx oxfmt — clean on all three files
  • Manual testing performed — n/a; requires a macOS 12 host to reproduce the dyld failure. The unit tests deterministically cover the detector, and the reporter's original recovery path (setting PRODUCER_HEADLESS_SHELL_PATH) confirms the escape hatch works.
  • Documentation updated — n/a; the hint IS the documentation.

Enterprise release / feature flag holdout

  • N/A — this PR doesn't touch enterprise-relevant surfaces
  • Ships to all users on merge without feature-flag holdout

UX/Screenshot recording

  • No UI impact — CLI error-message content only
  • Signed-off-by: Via -

…eadless-shell dyld crash

Field feedback (#hyperframes-cli-feedback ts=1784227832, darwin/x64,
macOS 12, HyperFrames CLI 0.7.60) hit
`dyld: Symbol not found: _kVTCompressionPropertyKey_ReferenceBufferCount`
from VideoToolbox when launching the pinned chrome-headless-shell
mac-152.0.7928.2. The symbol is macOS-13-only, so older hosts abort
the binary at dyld load before any browser process starts.

The reporter recovered by installing an older shell
(`@puppeteer/browsers install chrome-headless-shell@150`) and pointing
`PRODUCER_HEADLESS_SHELL_PATH` at it. Their check/snapshot commands
accepted that older cached shell (they do not force the pinned build),
but the render command requires v152 via `preferManagedChrome: true`
and could not fall back on its own. The generic "Try --docker" hint
didn't name any of the browser-path env vars.

Sibling failure mode to the download-time hint added in #2443 and the
closed-with-invite #2078 (SIGTRAP at launch on macOS arm64), and the
in-flight #2481 (Windows STATUS_STACK_BUFFER_OVERRUN); same
`HYPERFRAMES_BROWSER_PATH` remediation, different trigger + platform.

The match is gated on:
1. Puppeteer launch-failure wrapper text
2. dyld Symbol-not-found signal
3. a macOS-13-only symbol OR the VideoToolbox framework

so unrelated darwin launch failures do not mis-fire the hint. The
symbol name is macOS-version-specific by construction — if a user's
dyld cannot find `_kVTCompressionPropertyKey_ReferenceBufferCount`
their host is <13, no separate `os.release()` gate needed.

- Signed-off-by: Via -

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — fix(cli): surface HYPERFRAMES_BROWSER_PATH hint on macOS <13 chrome-headless-shell dyld crash

Scope: 3 files (+182/−0) — new detector + remediation module, test file, one integration site in render.ts.

SSOT analysis

No violations. The detector is self-contained — no shared predicates with the Linux chromeLaunchRemediation. Different platforms, different error signals, different remediation content. The fallow-ignore-file code-duplication pragma is appropriate (structural similarity with the Linux detector, genuinely different content).

What I checked

  1. Detector logic. Three-gate AND chain: Failed to launch the browser processSymbol not found: → (_kVTCompressionPropertyKey_ReferenceBufferCount OR VideoToolbox). The first two gates prevent false positives from unrelated errors; the third scopes to the macOS-13+ framework family. The OR on the third gate is intentional — future pinned builds may hit sibling VT symbols from the same framework, and the remediation (install an older shell) is the same for all of them.

  2. Platform gating. isMacosOldChromeCrashError is pure string matching (testable anywhere). macosOldChromeCrashRemediation gates on process.platform !== "darwin". Clean separation — the detector is logic, the remediation is action.

  3. Integration cascade. In handleRenderError: Linux check → macOS check → generic fallback. Each detector is self-scoping (Linux won't fire on darwin, macOS won't fire on Linux), so the order is for readability, not correctness. Neither matching → generic errorBox("Render failed", ...).

  4. Test coverage. 11 cases:

    • Positive: exact reporter error, VideoToolbox-only variant
    • Negative: non-launch error, unrelated framework (libc++.dylib), symbol without launch wrapper, generic unrelated error
    • Platform: off-darwin returns undefined, on-darwin returns remediation with both env vars named
    • Content: asserts HYPERFRAMES_BROWSER_PATH, PRODUCER_HEADLESS_SHELL_PATH, and chrome-headless-shell@150 are all present in the output
  5. No os.release() gate needed. The symbol _kVTCompressionPropertyKey_ReferenceBufferCount is itself the version discriminator — it only exists on macOS 13+, so a dyld failure to find it IS the version proof. The PR description explains this well; the comment in the source explains it well. No redundant check needed.

  6. Remediation path is x64-specific (cache path includes chrome-headless-shell-mac-x64). Reasonable — the field signal is from darwin/x64, and arm64 Macs shipped with macOS 11 but have been on 13+ since launch year. An arm64 user on macOS <13 would be extremely rare.

Verdict

Clean, well-scoped fix. Detector is tight (three-gate AND), platform-gated, and well-tested. Slots into the existing remediation cascade without touching Linux or Windows paths.

— Miga

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