denoland/deno · error · Error
Destroy hook of "${id}" errored
Error message
Destroy hook of "${id}" errored What it means
Thrown when a rule's optional `destroy()` hook raises. During lint setup (cli/js/40_lint.js, runPluginsForFile) destroy hooks are collected and each is wrapped so a failure is reported as 'Destroy hook of "<plugin>/<rule>" errored' with the original error as `cause`. Destroy runs after linting a file finishes, for cleanup.
Source
Thrown at cli/js/40_lint.js:1305
});
}
};
if (isExit) {
info.exit = wrapped;
} else {
info.enter = wrapped;
}
}
}
if (typeof rule.destroy === "function") {
const destroyFn = rule.destroy.bind(rule);
destroyFns.push(() => {
try {
destroyFn(ruleCtx);
} catch (err) {
throw new Error(`Destroy hook of "${id}" errored`, { cause: err });
}
});
}
}
}
// Create selectors
/** @type {TransformFn} */
const toElem = (str) => {
const id = ctx.typeByStr.get(str);
return id === undefined ? 0 : id;
};
/** @type {TransformFn} */
const toAttr = (str) => {
const id = ctx.propByStr.get(str);
return id === undefined ? 0 : id;
};
View on GitHub (pinned to 89f33cbef2)
Solutions
- Look at err.cause for the real stack inside your destroy() implementation
- Make destroy defensive: only clean up what was actually initialized, and tolerate partially-populated state
- If the hook has nothing to clean up, remove it entirely
Example fix
// before
destroy(ctx) {
this.seen.forEach((n) => n.parent.unregister(this));
},
// after
destroy(ctx) {
for (const entry of this.acquired) entry.release();
}, Defensive patterns
Strategy: try-catch
Try / catch
try {
await runLint(fixture);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Destroy hook of")) {
// inspect err.cause for the real cleanup failure
console.error("cleanup bug:", err.cause ?? err);
}
throw err;
} Prevention
- Make destroy() idempotent and tolerant of partially-initialized state
- Only acquire resources in create() that destroy() actually needs to release
When it happens
Trigger: A rule defines `destroy(ctx)` and the function throws: releasing a resource that was never acquired, referencing state that a crashed visitor left inconsistent, or calling a method on undefined cleanup state.
Common situations: Rules that accumulate per-file state (maps of imports, reported spans) and clean up in destroy; a destroy hook that assumes create() ran successfully for every file; refactoring a rule and forgetting destroy still reads an old field.
Related errors
- Visitor "${name}" of plugin "${id}" errored
- ${prefix}Linter plugin name must only contain lowercase lett
- ${prefix}Linter plugin name must start and end with a lowerc
- ${prefix}Linter plugin name must not have consequtive hyphen
- ${prefix}Linter plugin rules must be an object
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/13fec3ea501db958.
Report an issue: GitHub.