angular/components · error

ProxyZoneSpec is needed for the test harnesses but could not

Error message

ProxyZoneSpec is needed for the test harnesses but could not be found. Please make sure that your environment includes zone.js/dist/zone-testing.js

What it means

The Component Dev Kit (CDK) test harnesses require zone.js's ProxyZoneSpec to intercept and await async tasks during change detection. This error means Zone['ProxyZoneSpec'] is undefined at runtime because the zone-testing bundle was never loaded. The library throws early in harness setup rather than silently mis-behaving.

Source

Thrown at src/cdk/testing/testbed/task-state-zone-interceptor.ts:85

   * Sets up the custom task state Zone interceptor in the  `ProxyZone`. Throws if
   * no `ProxyZone` could be found.
   * @returns an observable that emits whenever the task state changes.
   */
  static setup(): Observable<TaskState> {
    if (Zone === undefined) {
      throw Error(
        'Could not find ZoneJS. For test harnesses running in TestBed, ' +
          'ZoneJS needs to be installed.',
      );
    }

    // tslint:disable-next-line:variable-name
    const ProxyZoneSpec = (Zone as any)['ProxyZoneSpec'] as ProxyZoneStatic | undefined;

    // If there is no "ProxyZoneSpec" installed, we throw an error and recommend
    // setting up the proxy zone by pulling in the testing bundle.
    if (!ProxyZoneSpec) {
      throw Error(
        'ProxyZoneSpec is needed for the test harnesses but could not be found. ' +
          'Please make sure that your environment includes zone.js/dist/zone-testing.js',
      );
    }

    // Ensure that there is a proxy zone instance set up, and get
    // a reference to the instance if present.
    const zoneSpec = ProxyZoneSpec.assertPresent() as PatchedProxyZone;

    // If there already is a delegate registered in the proxy zone, and it
    // is type of the custom task state interceptor, we just use that state
    // observable. This allows us to only intercept Zone once per test
    // (similar to how `fakeAsync` or `async` work).
    if (zoneSpec[stateObservableSymbol]) {
      return zoneSpec[stateObservableSymbol]!;
    }

    // Since we intercept on environment creation and the fixture has been

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Add 'node_modules/zone.js/dist/zone-testing.js' (zone.js < 0.11) or 'node_modules/zone.js/plugins/zone-testing' to the 'files'/'testFiles' list in angular.json, karma.conf.js, or your Jest/Vitest setup
  2. Verify Zone is actually loaded before harnesses: import 'zone.js' first, then zone-testing
  3. If using zone.js >= 0.11, update the old dist/ path which silently fails to register ProxyZoneSpec
  4. If the app is intentionally zoneless, load zone-testing in the test setup only, or use the harness-free querying APIs

Example fix

// before (angular.json)
"test": { "options": { "src": ["test.ts"] } }
// karma.conf.ts files: missing zone-testing
// after
"files": [
  "node_modules/zone.js/dist/zone.js",
  "node_modules/zone.js/dist/zone-testing.js"
]
Defensive patterns

Strategy: validation

Validate before calling

if (!(Zone as any)?.['ProxyZoneSpec']) {
  throw new Error('zone.js/dist/zone-testing.js is not loaded; harnesses cannot run');
}
// or check before creating harnesses in a shared test util:
expect((Zone as any)['ProxyZoneSpec']).toBeTruthy();

Type guard

const hasProxyZone = (): boolean =>
  typeof Zone !== 'undefined' && !!(Zone as any)['ProxyZoneSpec'];

Try / catch

try {
  await loader.getHarness(MyHarness);
} catch (e) {
  if ((e as Error).message.includes('ProxyZoneSpec is needed')) {
    fail('Add zone.js/dist/zone-testing.js to your test setup');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling any TestbedHarnessEnvironment API (e.g. loader(), harnessForFixture) or creating a harness whose setup path reaches TaskStateZoneInterceptor.setup when zone.js/dist/zone-testing.js (or zone.js/plugins/zone-testing for newer versions) is not included in test files.

Common situations: Missing zone-testing.js entry in angular.json test file globs; switching from Karma to Jest/Vitest without loading zone testing manually; upgrading Angular/zone.js where the dist path changed (zone.js/dist/zone-testing.js -> zone.js/plugins/zone-testing.js in zone.js >= 0.11); running with zoneless/noop Zone config while harnesses still expect zone.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/f45fb004da436146. Report an issue: GitHub.