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
- Add `assertDestroyablesDestroyed()` in an afterEach hook (or after each test) to properly end the tracking session before the next `enableDestroyableTracking()` call
- Audit test setup for duplicate `enableDestroyableTracking()` calls (e.g. double-registered beforeEach hooks)
- Wrap tracking start/end in a helper that guarantees pairing even on test failure (try/finally)
- 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
- Always pair enableDestroyableTracking() with assertDestroyablesDestroyed() in setup/teardown hooks
- Use a single shared test helper for destroyable tracking across the suite
- Avoid enabling tracking in nested or overlapping scopes
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.