{"record":{"id":"e532b2d8236595a9","repo":"denoland/deno","slug":"linter-plugin-plugin-name-has-already-been-regi","errorCode":null,"errorMessage":"Linter plugin ${plugin.name} has already been registered","messagePattern":"Linter plugin (.+?) has already been registered","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/js/40_lint.js","lineNumber":507,"sourceCode":"    throw new Error(\n      `${prefix}Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).`,\n    );\n  }\n  if (plugin.name.startsWith(\"-\") || plugin.name.endsWith(\"-\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must start and end with a lowercase letter.`,\n    );\n  }\n  if (plugin.name.includes(\"--\")) {\n    throw new Error(\n      `${prefix}Linter plugin name must not have consequtive hyphens.`,\n    );\n  }\n  if (typeof plugin.rules !== \"object\") {\n    throw new Error(`${prefix}Linter plugin rules must be an object`);\n  }\n  if (state.installedPlugins.has(plugin.name)) {\n    throw new Error(`Linter plugin ${plugin.name} has already been registered`);\n  }\n  state.plugins.push(plugin);\n  state.installedPlugins.add(plugin.name);\n\n  return {\n    name: plugin.name,\n    ruleNames: Object.keys(plugin.rules),\n  };\n}\n\n/**\n * @param {AstContext} ctx\n * @param {number} idx\n * @returns {FacadeNode | null}\n */\nfunction getNode(ctx, idx) {\n  if (idx === AST_IDX_INVALID) return null;\n  const cached = ctx.nodes.get(idx);","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/js/40_lint.js#L489-L525","documentation":"Thrown by installPlugin() in cli/js/40_lint.js when a plugin with the same `name` was already registered in this linter instance (state.installedPlugins). Rule ids are namespaced as `pluginName/ruleName`, so a second plugin with the same name would collide with the first. Unlike the shape errors, this message carries no specifier prefix.","triggerScenarios":"Listing the same plugin twice in `lint.plugins` in deno.json (directly or via two re-exporting modules); shipping two different plugins that both declare name: \"recommended\"; importing a plugin and also bundling a local copy of it.","commonSituations":"A config that loads a plugin both from jsr/npm and from a local path; a monorepo where two teams wrote plugins with the same generic name (\"lint\", \"helpers\"); stale duplicate entries after refactoring the plugin list.","solutions":["Remove the duplicate entry from `lint.plugins` so each plugin name is registered once","If two distinct plugins share a name, rename one of them to a unique lowercase name","If you re-export plugins through a barrel module, make sure the barrel is not listed alongside its targets"],"exampleFix":"// deno.json before\n{\n  \"lint\": {\n    \"plugins\": [\"jsr:@corp/lint\", \"./lint/index.ts\"]\n  }\n}\n\n// deno.json after\n{\n  \"lint\": {\n    \"plugins\": [\"jsr:@corp/lint\"]\n  }\n}","handlingStrategy":"validation","validationCode":"const seen = new Set();\nfor (const p of plugins) {\n  if (seen.has(p.name)) {\n    throw new Error(`duplicate lint plugin name: '${p.name}' listed twice`);\n  }\n  seen.add(p.name);\n}","typeGuard":"function hasUniqueNames(plugins) {\n  const names = plugins.map((p) => p.name);\n  return new Set(names).size === names.length;\n}","tryCatchPattern":null,"preventionTips":["Keep `lint.plugins` in deno.json as a flat, deduplicated list — avoid barrels that re-export other listed plugins","Give every plugin a name tied to its package identity so two teams cannot collide accidentally"],"tags":["lint","plugin","duplicate","config"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}