{"record":{"id":"7b53723c1af2cf44","repo":"liriliri/eruda","slug":"tool-name-already-exists","errorCode":null,"errorMessage":"Tool ${name} already exists","messagePattern":"Tool (.+?) already exists","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/DevTools/DevTools.js","lineNumber":111,"sourceCode":"  }\n  toggle() {\n    return this._isShow ? this.hide() : this.show()\n  }\n  add(tool) {\n    const tab = this._tab\n\n    if (!(tool instanceof Tool)) {\n      const { init, show, hide, destroy } = new Tool()\n      defaults(tool, { init, show, hide, destroy })\n    }\n\n    const name = tool.name\n    if (!name) {\n      return logger.error('You must specify a name for a tool')\n    }\n\n    if (this._tools[name]) {\n      return logger.warn(`Tool ${name} already exists`)\n    }\n\n    const id = name.replace(/\\s+/g, '-')\n    this._$tools.prepend(`<div id=\"${c(id)}\" class=\"${c(id + ' tool')}\"></div>`)\n    tool.init(this._$tools.find(`.${c(id)}.${c('tool')}`), this)\n    tool.active = false\n    this._tools[name] = tool\n\n    if (name === 'settings') {\n      tab.append({\n        id: name,\n        title: name,\n      })\n    } else {\n      tab.insert(tab.length - 1, {\n        id: name,\n        title: name,\n      })","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/liriliri/eruda/blob/0c55928fec802fb2102934c4b5928686178b467b/src/DevTools/DevTools.js#L93-L129","documentation":"devtools.add(tool) registers a tool keyed by its `name` property in the internal `this._tools` map and creates a tab/panel for it. If a tool with the same name is already registered, the library refuses to add a duplicate and logs this warning instead, leaving the existing tool untouched. It's a guard against silent overwriting of an already-initialized tool panel.","triggerScenarios":"Calling devtools.add(tool) (or eruda.add(tool)) with a tool whose `name` matches a tool already added — e.g. calling add() twice with the same tool instance, adding two custom tools that share a name, re-running an init script that adds a built-in tool like 'settings' or 'elements' again, or hot module reloading re-executing the add() call without removing the old tool.","commonSituations":"HMR/dev-server reload re-running setup code; initializing eruda in multiple scripts or bundles that each call add(); writing a custom plugin whose name collides with a built-in tool name; accidentally passing the same tool object in a loop or an event handler that fires more than once.","solutions":["Check devtools.get(name) before calling add() and skip if the tool already exists.","Call devtools.remove(name) before re-adding the tool, or use devtools.removeAll() during teardown.","Rename your custom tool so its `name` is unique and doesn't collide with built-in tools.","Guard the add() call so it runs only once (e.g. a module-level flag or init-once pattern) to survive HMR.","If the duplicate add is intentional and harmless, the warning can be safely ignored — the original tool stays active."],"exampleFix":"// before\neruda.add(myTool)\n\n// after\nif (!eruda.get('myTool')) {\n  eruda.add(myTool)\n}","handlingStrategy":"validation","validationCode":"if (!devtools.get(tool.name)) {\n  devtools.add(tool)\n}","typeGuard":"function canAdd(devtools, tool) {\n  return tool && typeof tool.name === 'string' && tool.name.length > 0 && !devtools.get(tool.name);\n}","tryCatchPattern":null,"preventionTips":["Always guard add() with a get(name) existence check.","Give custom tools unique names that don't clash with built-in tool names.","Wrap setup code in an init-once flag so HMR or double scripts don't add twice.","Pair every add() with a remove() in teardown logic."],"tags":["devtools","duplicate-registration","plugin","warning"],"backgroundTag":"duplicate-tool-registration","analyzedSha":"0c55928fec802fb2102934c4b5928686178b467b","analyzedAt":"2026-09-01T18:07:51.703Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}