angular/components · error

Harness is attempting to use a fixture that has already been

Error message

Harness is attempting to use a fixture that has already been destroyed.

What it means

TestbedHarnessEnvironment keeps a reference to the ComponentFixture and stabilizes it before harness operations (auto change detection). Once the fixture is destroyed (e.g. afterEach teardown or fixture.destroy()), any harness use that triggers forceStabilize throws this error. It prevents interacting with a dead fixture's DOM.

Source

Thrown at src/cdk/testing/testbed/testbed-harness-environment.ts:171

  static async harnessForFixture<T extends ComponentHarness>(
    fixture: ComponentFixture<unknown>,
    harnessType: ComponentHarnessConstructor<T>,
    options?: TestbedHarnessEnvironmentOptions,
  ): Promise<T> {
    const environment = new TestbedHarnessEnvironment(fixture.nativeElement, fixture, options);
    await environment.forceStabilize();
    return environment.createComponentHarness(harnessType, fixture.nativeElement);
  }

  /**
   * Flushes change detection and async tasks captured in the Angular zone.
   * In most cases it should not be necessary to call this manually. However, there may be some edge
   * cases where it is needed to fully flush animation events.
   */
  async forceStabilize(): Promise<void> {
    if (!disableAutoChangeDetection) {
      if (this._destroyed) {
        throw Error('Harness is attempting to use a fixture that has already been destroyed.');
      }

      await detectChanges(this._fixture);
    }
  }

  /**
   * Waits for all scheduled or running async tasks to complete. This allows harness
   * authors to wait for async tasks outside of the Angular zone.
   *
   * This only works when Zone.js is present _and_ patches are applied to the test framework
   * by `zone.js/testing` (Jasmine and Jest only) or another script.
   */
  async waitForTasksOutsideAngular(): Promise<void> {
    // If we run in the fake async zone, we run "flush" to run any scheduled tasks. This
    // ensures that the harnesses behave inside of the FakeAsyncTestZone similar to the
    // "AsyncTestZone" and the root zone (i.e. neither fakeAsync or async). Note that we
    // cannot just rely on the task state observable to become stable because the state will

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Recreate the fixture and harness inside each test (beforeEach) instead of reusing after destroy
  2. Ensure all harness interactions finish before fixture.destroy() is called (await them in the test body)
  3. Do not call fixture.destroy() manually mid-test while harnesses are still in use
  4. Check for async operations that outlive the test (unawaited promises, timers) that touch the fixture after teardown

Example fix

// before
let harness: ButtonHarness;
beforeEach(async () => { harness = await loader.getHarness(ButtonHarness); });
// after fixture destroyed in afterEach, using harness in next test throws
// after
it('...', async () => {
  const harness = await loader.getHarness(ButtonHarness); // fresh per test
  await harness.click();
});
Defensive patterns

Strategy: validation

Validate before calling

// before using a harness
if (fixture.isDestroyed ?? false) {
  throw new Error('Fixture already destroyed; recreate fixture and harness');
}
// Track destruction explicitly:
let destroyed = false;
afterEach(() => { destroyed = true; fixture.destroy(); });

Type guard

function canUseFixture(f: ComponentFixture<unknown>): boolean {
  return !f.isDestroyed;
}

Try / catch

try {
  await harness.getText();
} catch (e) {
  if ((e as Error).message.includes('already been destroyed')) {
    // recreate fixture + harness and retry once
    fixture = TestBed.createComponent(MyComp);
    loader = TestbedHarnessEnvironment.loader(fixture);
    harness = await loader.getHarness(MyHarness);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling any harness method (click, getText, setInputValue, etc.) or forceStabilize after fixture.destroy(); using harnesses created via harnessForFixture/getAllRawElements after the TestBed teardown; capturing a harness in a variable and using it in a later test.

Common situations: Storing harnesses at describe() scope and reusing across it() blocks; awaiting long-running assertions after Angular's autoDestroy on test end; creating harnesses in beforeEach but consuming after an explicit destroy; TestBed auto-detecting changes then autoDestroy running before a pending promise resolves.

Related errors


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