{"record":{"id":"228010af1e81cfcc","repo":"ruvnet/ruflo","slug":"plugin-name-already-registered","errorCode":null,"errorMessage":"Plugin ${name} already registered","messagePattern":"Plugin (.+?) already registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts","lineNumber":292,"sourceCode":"   */\n  async register(\n    plugin: IPlugin | PluginFactory,\n    config?: Partial<PluginConfig>\n  ): Promise<void> {\n    // Resolve factory if needed\n    const resolvedPlugin = typeof plugin === 'function' ? await plugin() : plugin;\n\n    // Validate plugin\n    if (!validatePlugin(resolvedPlugin)) {\n      throw new Error('Invalid plugin: does not implement IPlugin interface');\n    }\n\n    const name = resolvedPlugin.metadata.name;\n    const version = resolvedPlugin.metadata.version;\n\n    // Check for duplicates\n    if (this.plugins.has(name)) {\n      throw new Error(`Plugin ${name} already registered`);\n    }\n\n    // Check max plugins\n    if (this.config.maxPlugins && this.plugins.size >= this.config.maxPlugins) {\n      throw new Error(`Maximum plugin limit (${this.config.maxPlugins}) reached`);\n    }\n\n    // Check core version compatibility\n    if (resolvedPlugin.metadata.minCoreVersion) {\n      if (!satisfiesVersion(`>=${resolvedPlugin.metadata.minCoreVersion}`, this.config.coreVersion)) {\n        throw new Error(\n          `Plugin ${name} requires core version >= ${resolvedPlugin.metadata.minCoreVersion}, ` +\n          `but current version is ${this.config.coreVersion}`\n        );\n      }\n    }\n    if (resolvedPlugin.metadata.maxCoreVersion) {\n      if (!satisfiesVersion(`<=${resolvedPlugin.metadata.maxCoreVersion}`, this.config.coreVersion)) {","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts#L274-L310","documentation":"Thrown by EnhancedPluginRegistry.register() when a plugin with the same metadata.name is already present in the plugins map. The registry keys plugins strictly by name; the duplicate check runs right after interface validation and before max-plugins and version checks, so a duplicate is always rejected outright.","triggerScenarios":"Calling register(plugin) twice for the same plugin; registering two different plugins that both declare metadata.name 'my-plugin'; re-running bootstrap code (e.g. tests with a shared registry) that registers plugins again; a hot-reload path that registers the new version without unregistering the old one.","commonSituations":"Test files each importing a global registry fixture; watch-mode/HMR re-executing setup; plugin bundles that self-register on import being imported twice; copy-pasted plugins keeping the same name field.","solutions":["Unregister the existing plugin first (await registry.unregister(name)) or reuse the already-registered instance instead of re-registering","Check registration state before calling register (via the registry's list API) and make registration idempotent in setup code","Give each distinct plugin a unique metadata.name"],"exampleFix":"// before\nawait registry.register(pluginA); // name: 'logger'\nawait registry.register(pluginB); // also name: 'logger' -> throws\n\n// after\nawait registry.register(pluginA);\nconst registered = (await registry.list()).map(p => p.metadata.name);\nif (!registered.includes('logger')) {\n  await registry.register(pluginB);\n}\n// or: await registry.unregister('logger'); await registry.register(pluginB);","handlingStrategy":"validation","validationCode":"const name = plugin.metadata.name;\nconst registered = (await registry.list()).map(p => p.metadata.name);\nif (registered.includes(name)) {\n  await registry.unregister(name); // or skip if already loaded\n}\nawait registry.register(plugin);","typeGuard":null,"tryCatchPattern":"try {\n  await registry.register(plugin);\n} catch (err) {\n  if (err instanceof Error && /already registered$/.test(err.message)) {\n    return; // idempotent bootstrap\n  }\n  throw err;\n}","preventionTips":["Run registration from a single bootstrap function; never self-register on module import","Check the registered set before registering, especially in tests and watch-mode","Give every plugin a globally unique metadata.name (scope-prefix with team or project names)"],"tags":["duplicate-registration","plugin-registry","typescript"],"backgroundTag":"duplicate-plugin-registration","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}