emberjs/ember.js · error · Error
Attempted to assert destroyables destroyed, but you did not
Error message
Attempted to assert destroyables destroyed, but you did not start a destroyable test. Did you forget to call `enableDestroyableTracking()`
What it means
`assertDestroyablesDestroyed()` verifies that all destroyables created during a tracking session were destroyed, but only works when tracking was started via `enableDestroyableTracking()`. If the `isTesting` flag is false when it is called, the library throws because there is nothing to assert against. This guards against asserting in production or outside a tracking session.
Source
Thrown at packages/@glimmer/destroyable/index.ts:295
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>>;
DESTROYABLE_META = new WeakMap();
let undestroyed: object[] = [];
map.forEach((meta) => {
if (meta.state !== DESTROYED_STATE) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
undestroyed.push(meta.source!);
}
});
View on GitHub (pinned to 26f97246a8)
Solutions
- Call `enableDestroyableTracking()` in beforeEach before asserting in afterEach
- Ensure `assertDestroyablesDestroyed()` is only called once per tracking session
- Guard the afterEach with a flag or check that tracking was actually started in that test
- Initialize tracking in a shared test helper so setup/teardown order is guaranteed
Example fix
// before
afterEach(() => { assertDestroyablesDestroyed(); });
// after
beforeEach(() => { enableDestroyableTracking(); });
afterEach(() => { assertDestroyablesDestroyed(); }); Defensive patterns
Strategy: validation
Validate before calling
let trackingStarted = false;
afterEach(function () {
if (trackingStarted) assertDestroyablesDestroyed();
trackingStarted = false;
}); Type guard
const canAssertDestroyed = () => trackingStarted === true;
Try / catch
try { assertDestroyablesDestroyed(); } catch (e) { if (/did not start a destroyable test/.test(e.message)) { enableDestroyableTracking(); assertDestroyablesDestroyed(); } else throw e; } Prevention
- Call enableDestroyableTracking() in beforeEach whenever the assertion runs in afterEach
- Track session state in your test helper to make teardown conditional
- Never assert in production code paths where tracking is off
When it happens
Trigger: Calling `assertDestroyablesDestroyed()` before ever calling `enableDestroyableTracking()`, or after a previous `assertDestroyablesDestroyed()` already reset `isTesting` to false (double assertion).
Common situations: afterEach hooks that call the assertion unconditionally even for tests that never enabled tracking; assertion order bugs where teardown runs before setup.
Related errors
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/4ad71522472daa15.
Report an issue: GitHub.