denoland/deno · error · Error
Visitor "${name}" of plugin "${id}" errored
Error message
Visitor "${name}" of plugin "${id}" errored What it means
Thrown at lint-run time in runPluginsForFile (cli/js/40_lint.js) when a rule's visitor callback raises. The linter wraps every enter/exit visitor so the error names the rule and plugin ("Visitor \"<rule>\" of plugin \"<plugin>/<rule>\" errored") and attaches the original exception as `cause`. This is a wrapper: the real bug is in your visitor code, retrievable via err.cause.
Source
Thrown at cli/js/40_lint.js:1285
const key = selectors[j];
let info = bySelector.get(key);
if (info === undefined) {
info = { enter: NOOP, exit: NOOP };
bySelector.set(key, info);
}
const prevFn = isExit ? info.exit : info.enter;
/**
* @param {*} node
*/
const wrapped = (node) => {
prevFn(node);
try {
fn(node);
} catch (err) {
throw new Error(`Visitor "${name}" of plugin "${id}" errored`, {
cause: err,
});
}
};
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);View on GitHub (pinned to 89f33cbef2)
Solutions
- Inspect err.cause (and its stack) — it points at the exact line in your visitor, not at this wrapper message
- Narrow inside the visitor: check node.type / property existence before accessing, or tighten the selector so only matching shapes reach the callback
- Reproduce with `deno lint` on the single file that triggered it, then add that file as a fixture for the rule
Example fix
// before
"CallExpression:exit"(node) {
reportIfUpperCase(node.callee.name);
},
// after
"CallExpression:exit"(node) {
if (node.callee.type !== "Identifier") return;
reportIfUpperCase(node.callee.name);
}, Defensive patterns
Strategy: try-catch
Validate before calling
// smoke-test the plugin in CI: lint a fixture that exercises every visitor key // $ deno lint --config deno.json ./plugin_tests/fixtures/edge_cases.ts
Try / catch
try {
await runLint(fixtures); // deno lint over representative files
} catch (err) {
// the wrapper names the rule; err.cause holds the real stack
if (err instanceof Error && /Visitor ".+" of plugin ".+" errored/.test(err.message)) {
console.error("rule bug:", err.cause ?? err);
}
throw err;
} Prevention
- Validate node shape (node.type, property existence) at the top of every visitor before deep access
- Keep a fixture corpus covering edge-case syntax for each rule and lint it in CI
- Never let a visitor throw deliberately for control flow — use ctx.report
When it happens
Trigger: Any exception inside a `create(ctx)` visitor function while traversing a file: calling a method on undefined (e.g. node.callee.name on a node without callee), accessing properties of a node type that doesn't have them, or throwing intentionally without wrapping. Fires per file visited, during `deno lint` on real source files (not at plugin load time).
Common situations: A visitor assumes a specific node shape (e.g. MemberExpression) but the selector also matched a different shape; a rule that reads node.parent or sibling nodes that can be null; rules tested only on simple files crashing on edge-case syntax (decorators, optional chaining, directives).
Related errors
- Destroy hook of "${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/bff530b590f77598.
Report an issue: GitHub.