{"record":{"id":"bff530b590f77598","repo":"denoland/deno","slug":"visitor-name-of-plugin-id-errored","errorCode":null,"errorMessage":"Visitor \"${name}\" of plugin \"${id}\" errored","messagePattern":"Visitor \"(.+?)\" of plugin \"(.+?)\" errored","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/js/40_lint.js","lineNumber":1285,"sourceCode":"          const key = selectors[j];\n\n          let info = bySelector.get(key);\n          if (info === undefined) {\n            info = { enter: NOOP, exit: NOOP };\n            bySelector.set(key, info);\n          }\n          const prevFn = isExit ? info.exit : info.enter;\n\n          /**\n           * @param {*} node\n           */\n          const wrapped = (node) => {\n            prevFn(node);\n\n            try {\n              fn(node);\n            } catch (err) {\n              throw new Error(`Visitor \"${name}\" of plugin \"${id}\" errored`, {\n                cause: err,\n              });\n            }\n          };\n\n          if (isExit) {\n            info.exit = wrapped;\n          } else {\n            info.enter = wrapped;\n          }\n        }\n      }\n\n      if (typeof rule.destroy === \"function\") {\n        const destroyFn = rule.destroy.bind(rule);\n        destroyFns.push(() => {\n          try {\n            destroyFn(ruleCtx);","sourceCodeStart":1267,"sourceCodeEnd":1303,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/js/40_lint.js#L1267-L1303","documentation":"When a lint plugin rule's visitor callback (an enter handler or a \":exit\" handler) throws while visiting a node, the linter wraps the exception as `Visitor \"<ruleName>\" of plugin \"<pluginName>/<ruleName>\" errored` (cli/js/40_lint.js). The original exception is preserved on `error.cause`. This identifies which rule failed mid-file, since a raw stack from plugin code alone would not say which selector key triggered it.","triggerScenarios":"A rule's create(ctx) returns a visitor like `{ \"VariableDeclaration:exit\": (node) => ... }` and the callback throws on some node during `deno lint` — e.g. reading node.declarations[0].init.type when init is null, passing bad arguments to ctx.report, or any bug in the plugin's logic for an edge-case AST shape.","commonSituations":"Visitor assumes optional AST fields always exist (init, directive, typeParameters, elements containing null holes); AST shape differences after a Deno upgrade; plugin logic that only fails on unusual syntax (decorators, optional chaining, top-level await).","solutions":["Inspect `error.cause` (not just the message) — run deno lint and print the cause chain to get the original stack from your plugin.","Guard optional AST fields in the visitor (check for null/undefined before reading .type or indices).","Reproduce with the smallest file that triggers it, then fix the plugin code.","Lint the failing file with the plugin disabled to confirm the plugin (not the file) is the problem."],"exampleFix":"// before\n\"VariableDeclaration:exit\"(node) {\n  if (node.declarations[0].init.type === \"Literal\") {\n    ctx.report({ node, message: \"no literal init\" });\n  }\n},\n\n// after\n\"VariableDeclaration:exit\"(node) {\n  const init = node.declarations[0]?.init;\n  if (init !== null && init !== undefined && init.type === \"Literal\") {\n    ctx.report({ node, message: \"no literal init\" });\n  }\n},","handlingStrategy":"try-catch","validationCode":"// Fuzz the plugin over a fixture corpus during development so visitor bugs\n// surface in CI instead of during a real lint run:\n// deno lint --plugin=./my-plugin.js ./fixtures/","typeGuard":null,"tryCatchPattern":"// In tooling that drives the linter programmatically:\ntry {\n  runLint(files);\n} catch (err) {\n  const root = err.cause ?? err; // original plugin exception\n  console.error(`lint plugin failure: ${err.message}`);\n  console.error(root.stack ?? root);\n  Deno.exit(1);\n}","preventionTips":["Treat every AST field as optional: use ?. and explicit null checks before reading .type or indexing arrays.","Run the plugin against a fixture corpus (unusual syntax, empty programs, edge-case nodes) in CI.","When this error fires, always inspect error.cause — the wrapper message alone has no stack from your code."],"tags":["deno","deno-lint","lint-plugin","visitor","error-cause"],"backgroundTag":"plugin-callback-threw","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}