Skip to content

Flaky frontend unit tests: "Maximum call stack size exceeded" from unbounded ProxyZone nesting in test-zone-setup.ts #6593

Description

@mengw15

What happened?

Frontend unit tests intermittently fail on CI with RangeError: Maximum call stack size exceeded in trivial, unrelated specs — e.g. notification.service.spec.ts and filters-instructions.component.spec.ts, each a single should (be) create(d) test. Those specs are not the cause; they are victims of stack exhaustion inside zone.js.

The root cause is in frontend/src/test-zone-setup.ts, which patches global it/test so every test body runs in a freshly-forked ProxyZone:

// frontend/src/test-zone-setup.ts
const zone = Zone.current.fork(new ProxyZoneSpec());   // forks from Zone.current, a NEW instance per test
zone.run(() => { const result = fn.apply(this, args); /* ... */ });

This diverges from zone.js's own test-framework integrations, which fork one ProxyZone from the root zone, reuse it for every spec, and swap its delegate per test via setDelegate/resetDelegate (see zone.js/fesm2015/zone-testing.js: jasmine ambientZone.fork(...), jest rootZone.fork(...), fakeAsync Zone.root.fork(...)). Because test-zone-setup.ts forks from Zone.current and never reuses or resets the proxy, ProxyZones can nest without bound as a worker executes test files; every zone.run() then recurses through the whole ProxyZoneSpec.onInvoke → _ZoneDelegate.invoke delegate chain, and once the chain is deep enough it overflows the stack.

The depth reached depends on how Vitest packs test files onto workers (which varies with CPU/worker count), so the failure is flaky and OS-dependent — observed on macos-latest while ubuntu-latest/windows-latest pass on the same commit. When it hits inside the merge queue, the PR is evicted from the queue even though its own PR checks are green. (Related earlier zone-leak history: #4998.)

How to reproduce?

Flaky; it depends on per-worker test-file packing and is not deterministically reproducible from a single spec. Observed in CI:

  • Job build / frontend (macos-latest, 24.10.0) on the merge-queue branch gh-readonly-queue/main/pr-6570-547d5051...: 2 of 2902 tests failed, both RangeError: Maximum call stack size exceeded, in specs unrelated to the queued PRs.
  • The same commit passed on ubuntu-latest and windows-latest.

Expected: a unit test must not fail because of zone state accumulated by previously-run test files in the same worker.

Suggested fix: align test-zone-setup.ts with zone.js's canonical pattern — fork a single ProxyZone from Zone.root once, reuse it for all tests, and resetDelegate() between tests, instead of forking a new proxy from Zone.current per test. This also keeps fakeAsync/waitForAsync working, since they rely on locating and setting the delegate of the shared proxy zone.

Version/Branch

main

What browsers are you seeing the problem on?

N/A — CI unit tests (Vitest / jsdom).

Relevant log output

 FAIL   gui  src/app/common/service/notification/notification.service.spec.ts > NotificationService > should be created
RangeError: Maximum call stack size exceeded
 ❯ src/test-zone-setup.ts:53:33
 ❯ _ZoneDelegate.invoke node_modules/zone.js/fesm2015/zone.js:398:28
 ❯ ProxyZoneSpec.onInvoke node_modules/zone.js/fesm2015/zone-testing.js:2132:39
 ❯ _ZoneDelegate.invoke node_modules/zone.js/fesm2015/zone.js:397:34
 ❯ ProxyZoneSpec.onInvoke node_modules/zone.js/fesm2015/zone-testing.js:2132:39
 ... (repeats until the stack overflows)

 FAIL   gui  src/app/dashboard/component/user/filters-instructions/filters-instructions.component.spec.ts > FiltersInstructionsComponent > should create
RangeError: Maximum call stack size exceeded
 ❯ _ZoneDelegate.invoke node_modules/zone.js/fesm2015/zone.js:398:28
 ❯ ProxyZoneSpec.onInvoke node_modules/zone.js/fesm2015/zone-testing.js:2132:39
 ... (repeats until the stack overflows)

 Test Files  2 failed | 175 passed (177)
      Tests  2 failed | 2898 passed | 2 skipped (2902)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions