emberjs/ember.js · error · Error

Attempted to start destroyable testing, but you did not end

Error message

Attempted to start destroyable testing, but you did not end the previous destroyable test. Did you forget to call `assertDestroyablesDestroyed()`

What it means

This error comes from @glimmer/destroyable's test tracking mode. `enableDestroyableTracking()` flips an `isTesting` flag; if it is called while a previous tracking session is still active, the library throws rather than silently discarding the previous session's destroyable meta. It exists to enforce that each tracking session is paired with `assertDestroyablesDestroyed()`.

Source

Thrown at packages/@glimmer/destroyable/index.ts:284

export function isDestroyed(destroyable: Destroyable) {
  let meta = peekDestroyableMeta(destroyable);

  return meta === undefined ? false : meta.state >= DESTROYED_STATE;
}

////////////

export let enableDestroyableTracking: undefined | (() => void);
export let assertDestroyablesDestroyed: undefined | (() => void);

if (DEBUG) {
  let isTesting = false;

  enableDestroyableTracking = () => {
    if (isTesting) {
      // Reset destroyable meta just in case, before throwing the error
      DESTROYABLE_META = new WeakMap();
      throw new Error(
        'Attempted to start destroyable testing, but you did not end the previous destroyable test. Did you forget to call `assertDestroyablesDestroyed()`'
      );
    }

    isTesting = true;
    DESTROYABLE_META = new Map();
  };

  assertDestroyablesDestroyed = () => {
    if (!isTesting) {
      throw new Error(
        'Attempted to assert destroyables destroyed, but you did not start a destroyable test. Did you forget to call `enableDestroyableTracking()`'
      );
    }

    isTesting = false;

    let map = DESTROYABLE_META as Map<Destroyable, DestroyableMeta<Destroyable>>;

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Add `assertDestroyablesDestroyed()` in an afterEach hook (or after each test) to properly end the tracking session before the next `enableDestroyableTracking()` call
  2. Audit test setup for duplicate `enableDestroyableTracking()` calls (e.g. double-registered beforeEach hooks)
  3. Wrap tracking start/end in a helper that guarantees pairing even on test failure (try/finally)
  4. Reset test state between suites so the module-level `isTesting` flag starts fresh

Example fix

// before
beforeEach(() => { enableDestroyableTracking(); });
// after
beforeEach(() => { enableDestroyableTracking(); });
afterEach(() => { assertDestroyablesDestroyed(); });
Defensive patterns

Strategy: validation

Validate before calling

let trackingActive = false;
function startTracking() {
  if (trackingActive) { assertDestroyablesDestroyed(); }
  enableDestroyableTracking();
  trackingActive = true;
}
function endTracking() {
  assertDestroyablesDestroyed();
  trackingActive = false;
}

Type guard

const isTrackingActive = () => destroyablesTestingFlag === true; // via a wrapper module that owns the state

Try / catch

try { enableDestroyableTracking(); runTest(); } catch (e) { if (!/did not end the previous/.test(e.message)) throw e; assertDestroyablesDestroyed(); enableDestroyableTracking(); runTest(); }

Prevention

When it happens

Trigger: Calling `enableDestroyableTracking()` twice without an intervening `assertDestroyablesDestroyed()` call (e.g. in a shared beforeEach, nested test setup, or a test helper that forgets the assertion step).

Common situations: Ember/Glimmer test suites where destroyable tracking is enabled per-test but the assertion helper was dropped from an afterEach hook, or a test throws before `assertDestroyablesDestroyed()` runs leaving `isTesting` true for subsequent tests.

Related errors


AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01). Data as JSON: /api/errors/3103419394a8fc33. Report an issue: GitHub.