{"record":{"id":"4bda853b5e757f71","repo":"ruvnet/ruflo","slug":"category-identifier-conflict-between-exis","errorCode":null,"errorMessage":"${category}: ${identifier} conflict between ${existing} and ${pluginName}","messagePattern":"(.+?): (.+?) conflict between (.+?) and (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts","lineNumber":760,"sourceCode":"        const existingNs = template\n          .replace('{plugin}', existing)\n          .replace('{name}', identifier);\n        if (!owners.has(existingNs)) {\n          this.renameInCache(category, identifier, existingNs);\n          owners.delete(identifier);\n          owners.set(existingNs, existing);\n        }\n\n        const newName = template\n          .replace('{plugin}', pluginName)\n          .replace('{name}', identifier);\n        owners.set(newName, pluginName);\n        return { ...item, name: newName, type: newName } as T;\n      }\n\n      case 'error':\n      default:\n        throw new Error(`${category}: ${identifier} conflict between ${existing} and ${pluginName}`);\n    }\n  }\n\n  private renameInCache(category: string, oldName: string, newName: string): void {\n    switch (category) {\n      case 'agentTypes':\n        this.agentTypesCache = this.agentTypesCache.map(t =>\n          t.type === oldName ? { ...t, type: newName } : t\n        );\n        break;\n      case 'taskTypes':\n        this.taskTypesCache = this.taskTypesCache.map(t =>\n          t.type === oldName ? { ...t, type: newName } : t\n        );\n        break;\n      case 'mcpTools':\n        this.mcpToolsCache = this.mcpToolsCache.map(t =>\n          t.name === oldName ? { ...t, name: newName } : t","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts#L742-L778","documentation":"Thrown by EnhancedPluginRegistry's extension-conflict resolution when two plugins contribute an extension with the same identifier (agentTypes, taskTypes, etc.) and the configured resolution strategy for that category is 'error'. The resolver switch handles rename/namespace/prefix/suffix strategies by rewriting the incoming identifier; 'error' (the default) refuses and names both the existing owner and the challenger in the message.","triggerScenarios":"Two registered plugins both declaring an agentType or taskType with the same identifier string while the registry config's conflictResolution for that category is 'error' or unset; registering a plugin whose extension names collide with an existing plugin's; plugin upgrades introducing an extension name another plugin already claims.","commonSituations":"Two teams' plugins independently shipping a 'researcher' agent type; a fork or customization re-declaring a stock extension identifier; enabling a third-party plugin pack whose identifiers clash with existing ones.","solutions":["Rename one plugin's extension identifier so contributions are unique","Configure a non-error conflict strategy for the category (rename/namespace/prefix/suffix) so the registry auto-disambiguates identifiers like '{plugin}-{name}'","Unregister the plugin you do not actually need if the duplicate is accidental"],"exampleFix":"// before\nconst registry = new EnhancedPluginRegistry({\n  coreVersion: '3.0.0',\n  // conflictResolution unset -> 'error' strategy\n});\n// both plugins declare agentType 'researcher' -> conflict throws\n\n// after\nconst registry = new EnhancedPluginRegistry({\n  coreVersion: '3.0.0',\n  conflictResolution: {\n    agentTypes: { strategy: 'prefix' },\n    taskTypes: { strategy: 'namespace' },\n  },\n});","handlingStrategy":"validation","validationCode":"// Pre-scan contributed extensions for collisions before initialize()\nconst seen = new Map<string, string>(); // identifier -> plugin name\nfor (const [pluginName, ids] of collectDeclaredExtensions(registeredPlugins)) {\n  for (const id of ids) {\n    if (seen.has(id)) {\n      throw new Error(`${id} declared by both ${seen.get(id)} and ${pluginName}`);\n    }\n    seen.set(id, pluginName);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await registry.initialize();\n} catch (err) {\n  if (err instanceof Error && /conflict between/.test(err.message)) {\n    // reconfigure the strategy to auto-disambiguate, then retry once\n    registryConfig.conflictResolution = { default: { strategy: 'prefix' } };\n    return initializeWithFreshRegistry();\n  }\n  throw err;\n}","preventionTips":["Namespace your plugin's extension identifiers (e.g. 'acme-researcher') by convention","Configure conflictResolution strategies (prefix/namespace/rename) instead of the default 'error' when loading third-party packs","Add a startup duplicate-extension scan so collisions surface with both owners named"],"tags":["naming-conflict","extension-registration","plugin-registry","typescript"],"backgroundTag":"extension-name-conflict","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}