LearnNewsExamplesServices
Frontmatter
id16151
titleHeaded E2E Chrome launches abort in macOS app registration
stateOpen
labels
bugdeveloper-experienceaitesting
assigneesneo-gpt-emmy
createdAtJul 30, 2026, 1:14 AM
updatedAtAug 24, 2026, 5:57 PM
githubUrlhttps://github.com/neomjs/neo/issues/16151
authorneo-gpt-emmy
commentsCount10
parentIssuenull
subIssues
16159 Presenting E2E launches a redundant no-op gl-probe Chrome owner
16161 E2E lifecycle receipt does not normalize rejected Chrome launch exits
17595 The launch-exit receipt omits the channel it already holds, so two seats spent a night distinguishing branded from bundled
17679 Browser launch receipts are overwritten before native correlation
subIssuesCompleted4
subIssuesTotal4
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Headed E2E Chrome launches abort in macOS app registration

Open Backlog/active-chunk-10 bugdeveloper-experienceaitesting
neo-gpt-emmy
neo-gpt-emmy commented on Jul 30, 2026, 1:14 AM

Context

After a headed Playwright run, macOS displayed “Google Chrome quit unexpectedly.” The initial question was whether Neo closes its custom Chrome windows too aggressively.

To assert where the failure occurs, I inspected the matching macOS diagnostic receipt rather than inferring from when the dialog became visible. The latest Google Chrome process launched at 2026-07-30 00:59:33.6584 +0200 and entered the crash reporter at 00:59:33.7120 — roughly 54 ms after launch. Its parent was Node, the process terminated itself with EXC_CRASH / SIGABRT, and the faulting main-thread stack is:

abort
___RegisterApplication_block_invoke
_RegisterApplication
TransformProcessType
ChromeMain

This is not an isolated receipt. The same seat contains 13 Google Chrome application-registration aborts between 2026-07-25 and 2026-07-30; 11 name Node as the direct parent. Most abort within 200 ms of launch. That falsifies “unclean close” as the explanation for this dialog family: the affected process dies during macOS application registration, before a Playwright browser transport can be established. It does not yet prove whether the trigger is rapid same-bundle relaunch, concurrent harness launches, the current two-project handoff, or a Chrome/macOS defect.

Current dev at be17257b3f gives every E2E run two branded-Chrome project lifecycles:

gl-probe project (channel: chrome)
→ close
→ chromium project (channel: chrome)

The presenting default makes no GPU claim, yet still launches the gl-probe browser so that the probe can report “nothing demanded.” Local Playwright 1.61.1 source already attempts Browser.close when a transport exists and only force-kills when no transport exists or graceful close times out. Playwright's public API likewise recommends closing explicitly created contexts before the browser: https://playwright.dev/docs/api/class-browser#browser-close.

Live latest-open sweep: checked the 20 newest open issues and the latest 30 A2A messages at 2026-07-30 01:13 CEST; no equivalent ticket or in-flight claim exists. Existing #15664 / #15813 cover GPU-process death (SIGTRAP), not this AppKit _RegisterApplication SIGABRT family.

The Problem

Intermittent branded-Chrome startup aborts leak macOS crash dialogs onto the operator's desktop. The timing is misleading: the dialog can surface when a test ends even though the recorded crash happened at a browser launch boundary.

The E2E result currently carries no retained lifecycle receipt that identifies:

  • which Playwright project/browser owner failed;
  • whether a prior same-bundle owner had fully exited;
  • whether another harness launched Chrome concurrently;
  • whether Playwright had a transport and used graceful close or fell back to force-kill.

A surviving project/browser can therefore continue producing test output while a sibling launch has already polluted the physical desktop. Repeated visual work across agents amplifies the same-bundle launch pressure.

The Architectural Reality

  • test/playwright/playwright.config.e2e.mjs owns the gl-probe → chromium dependency topology and selects local Google Chrome through channel: 'chrome'.
  • test/playwright/e2e/utils/gpuIntent.mjs decides whether the current profile actually claims accelerated GL.
  • test/playwright/e2e/gl.setup.mjs is an effect probe, but its setup-project placement necessarily creates a separate Playwright browser lifecycle.
  • Playwright Test owns its runner browser/context fixtures. Neo should not add process-name cleanup around them.
  • Any library-owned/custom browser launcher must close its explicit BrowserContext instances before Browser.close; forced termination is an escalation path, not the ordinary terminal.
  • macOS application registration is outside Neo's product runtime. The E2E harness owns launch topology, evidence, and collision avoidance — not AppKit internals.

The Fix

  1. Add a bounded macOS lifecycle falsifier using the current E2E launch arguments and a minimal page. It must distinguish three cells: one branded-Chrome launch, the current gl-probe → chromium handoff, and two concurrent same-bundle harness launches. Record process birth/exit order and crash-report deltas without reading personal browser content.
  2. Bind the repair to the first red cell:
    • if the intra-run handoff is red, eliminate the redundant branded-Chrome lifecycle (the presenting profile must not launch a no-op GL probe; an engine-profile effect probe must observe the browser that supplies the measured run or wait for proven native-owner retirement);
    • if only concurrent launches are red, add same-bundle launch admission or use a distinct Playwright-owned browser bundle for the probe/run, preserving the measured rendering contract;
    • if neither harness cell reproduces, retain the receipt and fail loud on the next observed early browser exit rather than guessing.
  3. Make early browser exit a first-class test failure containing project, launch profile, exit code/signal, transport-established state, and the bounded macOS crash signature when available.
  4. For every Neo-owned custom launcher touched by the chosen repair, close explicit contexts before the browser and await the graceful terminal. Disclose any timeout-driven forced kill.
  5. Preserve the operator's existing Chrome processes exactly; ownership is launch-receipt based, never process-name based.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Ordinary headed E2E launch playwright.config.e2e.mjs + selected launch args No redundant no-op branded-Chrome lifecycle; one presenting owner unless a measured requirement says otherwise Fail before product assertions when lifecycle is indeterminate Config JSDoc/comments Process-order receipt + repeated headed witness
Engine GL effect probe gl.setup.mjs + gpuIntent.mjs Observe the same measured launch contract without an unsafe same-bundle handoff Refuse benchmark attribution if GL cannot be observed Existing probe output Red/green lifecycle matrix + live GL receipt
Browser terminal Playwright-owned fixture or explicit custom launcher owner Explicit contexts settle, then graceful browser terminal; forced kill only after bounded timeout Fail loud with terminal receipt Owner JSDoc Unit falsifiers + macOS lifecycle receipt
Operator Chrome processes Pre-run native owner set Byte-for-byte owner-set preservation outside the exact launched test owners Abort cleanup on ambiguous ownership Test guidance Before/after owner-set diff

Decision Record impact

none.

Acceptance Criteria

  • A retained pre-fix macOS receipt reproduces or captures _RegisterApplication / TransformProcessType SIGABRT and attributes it to exactly one lifecycle cell.
  • The chosen repair is justified by that red cell and a matched green control; no speculative sleep is accepted without a measured native-retirement boundary.
  • Ordinary presenting E2E does not launch a browser solely for a no-op GL demand.
  • Engine-profile runs keep the existing fail-loud live-GL guarantee and do not silently probe a different browser contract.
  • An early Chrome exit before transport establishment fails the run with project/profile/exit provenance instead of leaving only a macOS dialog.
  • Explicit custom contexts are closed and awaited before their browser; timeout escalation is bounded and visible.
  • A repeated headed lifecycle witness creates no new application-registration crash receipt and no crash dialog.
  • Pre-existing operator Chrome owners/windows are unchanged; no killall, pkill, process-name kill, or title/URL inspection is introduced.
  • Existing launch-profile unit coverage and focused headed Workstation assertions remain green.

Out of Scope

  • Product-side docking behavior.
  • Reversing the presenting default delivered by #16138.
  • Suppressing macOS crash dialogs or deleting diagnostic reports.
  • Generic desktop/browser process management.
  • Reopening resolved GPU-crash tickets.

Avoided Traps

  • Do not treat when the dialog appears as when the process crashed.
  • Do not “fix” this by disabling Crashpad/crash reporting.
  • Do not kill all Google Chrome processes at test end.
  • Do not add an arbitrary delay without a red/green retirement measurement.
  • Do not conflate this SIGABRT application-registration family with the earlier GPU SIGTRAP family.

Related

  • #16128 / PR #16138 — presenting E2E default.
  • #16150 — falsified universal frame-rate-override premise.
  • #15664 / #15813 — distinct GPU-process crash family and live GL probe.

Origin Session ID: 019fac4d-7844-7422-9486-7f73ccf308f5

Retrieval Hint: "macOS Google Chrome RegisterApplication TransformProcessType SIGABRT Playwright lifecycle"

tobiu referenced in commit a75aabe - "fix(testing): skip no-op presenting GL probe (#16151) (#16160)" on Jul 30, 2026, 12:34 PM