angular/angular · error · Error

Missing ProxyZoneSpec

Error message

Missing ProxyZoneSpec

What it means

The vitest patch needs ProxyZoneSpec to create the proxy zone that isolates each vitest test's zone state. It checks Zone.ProxyZoneSpec right after marking vitest with __zone_patch__; if the proxy zone-spec module was never loaded, the property is undefined and it throws. Loading zone.js for vitest therefore requires the proxy spec to have been registered first.

Source

Thrown at packages/zone.js/lib/vitest/vitest.ts:61

 */
const CURRIED_MODIFIER_NAMES = ['skipIf', 'runIf', 'each', 'for'] as const;

type TEST_MODIFIER_NAME = (typeof DIRECT_MODIFIER_NAMES | typeof CURRIED_MODIFIER_NAMES)[number];

export function patchVitest(Zone: ZoneType): void {
  Zone.__load_patch('vitest', (context: any, Zone: TestingZoneType) => {
    // Vitest global variable set by the Vitest runner during test execution
    const vitestGlobal = context['vitest'] as {['__zone_patch__']?: boolean} | undefined;

    // Skip patching if vitest is not present or has already been patched
    if (typeof vitestGlobal === 'undefined' || vitestGlobal['__zone_patch__']) {
      return;
    }
    vitestGlobal['__zone_patch__'] = true;

    // Ensure other testing related Zone.js patches have been applied
    if (!Zone.ProxyZoneSpec) {
      throw new Error('Missing ProxyZoneSpec');
    }
    if (!Zone.SyncTestZoneSpec) {
      throw new Error('Missing SyncTestZoneSpec');
    }

    // Setup testing related Zone instances
    const rootZone = Zone.current;
    const syncZone = rootZone.fork(new Zone.SyncTestZoneSpec('vitest.describe'));
    const proxyZone = rootZone.fork(new Zone.ProxyZoneSpec());

    /**
     * Gets a function wrapping the body of a vitest `describe` block to execute in a
     * synchronous-only zone.
     */
    function wrapDescribeInZone(describeBody: Function): Function {
      // `describe` might be called without a body (e.g. `describe.todo`)
      if (typeof describeBody !== 'function') {
        return describeBody;

View on GitHub (pinned to 51cb07e980)

Solutions

  1. In vitest setupFiles, load the proxy spec before the vitest patch: import 'zone.js/lib/zone-spec/proxy' then 'zone.js/lib/vitest/vitest' (or the zone-testing equivalent that already contains it)
  2. Prefer the documented Angular vitest setup which wires the correct zone.js bundles
  3. After setup, assert (globalThis.Zone as any).ProxyZoneSpec is truthy in a smoke test

Example fix

// before (vitest.setup.ts)
import 'zone.js/lib/vitest/vitest'; // ProxyZoneSpec not yet registered

// after
import 'zone.js/lib/zone-spec/proxy';
import 'zone.js/lib/zone-spec/sync-test';
import 'zone.js/lib/vitest/vitest';
Defensive patterns

Strategy: validation

Validate before calling

// vitest setupFiles, ordered imports
import 'zone.js/lib/zone-spec/proxy';
import 'zone.js/lib/zone-spec/sync-test';
import 'zone.js/lib/vitest/vitest';
if (!(globalThis.Zone as any).ProxyZoneSpec) throw new Error('proxy spec missing');

Type guard

const hasProxyZoneSpec = (z: any): boolean =>
  typeof z?.ProxyZoneSpec === 'function';

Prevention

When it happens

Trigger: Setting up zone.js with vitest by loading only a vitest-specific patch file without 'zone.js/lib/zone-spec/proxy'; using a custom entry like zone-vitest.js that omits the proxy spec; importing the vitest patch before the proxy module. Fires only when global.vitest exists (inside a vitest run).

Common situations: Adopting vitest with Angular/zone-based code and hand-rolling the zone setup in vitest setupFiles instead of using the documented preset; partial migration where jest setup files are reused; tree-shaking or selective imports trimming the proxy spec out of the bundle.

Related errors


AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22). Data as JSON: /api/errors/802ca8f39ff2a178. Report an issue: GitHub.