{"record":{"id":"67251f8810c50a43","repo":"can1357/oh-my-pi","slug":"plugin-name-is-already-registered","errorCode":null,"errorMessage":"Plugin '${name}' is already registered","messagePattern":"Plugin '(.+?)' is already registered","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/mnemopi/src/core/plugins.ts","lineNumber":275,"sourceCode":"\toverride onInvalidate(_memoryId: string): void {}\n}\n\nexport type PluginConstructor<T extends MnemopiPlugin = MnemopiPlugin> = new (config?: PluginConfig) => T;\n\nexport class PluginManager {\n\tprivate readonly registry = new Map<string, PluginConstructor>();\n\tprivate readonly instances = new Map<string, MnemopiPlugin>();\n\tconstructor(private readonly pluginDir = DEFAULT_PLUGIN_DIR) {\n\t\tthis.registerPlugin(\"logging\", LoggingPlugin);\n\t\tthis.registerPlugin(\"metrics\", MetricsPlugin);\n\t\tthis.registerPlugin(\"filter\", FilterPlugin);\n\t\tthis.registerPlugin(\"compression\", CompressionPlugin);\n\t}\n\tregisterPlugin(name: string, pluginClass: PluginConstructor): void {\n\t\tif (typeof pluginClass !== \"function\" || !(pluginClass.prototype instanceof MnemopiPlugin)) {\n\t\t\tthrow new TypeError(\"pluginClass must be a MnemopiPlugin subclass\");\n\t\t}\n\t\tif (this.registry.has(name)) throw new ValueError(`Plugin '${name}' is already registered`);\n\t\tthis.registry.set(name, pluginClass);\n\t}\n\tloadPlugin(name: string, config: PluginConfig = {}): MnemopiPlugin {\n\t\tconst pluginClass = this.registry.get(name);\n\t\tif (pluginClass === undefined) throw new ValueError(`Plugin '${name}' is not registered`);\n\t\tif (this.instances.has(name)) throw new Error(`Plugin '${name}' is already loaded`);\n\t\tconst instance = new pluginClass(config);\n\t\tinstance.initialize();\n\t\tthis.instances.set(name, instance);\n\t\treturn instance;\n\t}\n\tunloadPlugin(name: string): void {\n\t\tconst instance = this.instances.get(name);\n\t\tif (instance === undefined) throw new ValueError(`Plugin '${name}' is not loaded`);\n\t\tthis.instances.delete(name);\n\t\tinstance.shutdown();\n\t}\n\tlistPlugins(): Array<Record<string, unknown>> {","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/core/plugins.ts#L257-L293","documentation":"PluginManager keeps a registry map of plugin names to constructors and forbids duplicate names so loadPlugin always resolves unambiguously. The ValueError fires on a second registerPlugin call with a name already in the registry — including the built-in names registered in the constructor.","triggerScenarios":"Calling registerPlugin(\"metrics\", ...) or registerPlugin(\"filter\", ...) or registerPlugin(\"compression\", ...) after the manager constructor has already registered those built-ins; registering the same custom name twice.","commonSituations":"Re-initializing or hot-reloading a manager/module so the constructor runs twice; creating a second manager instance in the same process when the registry is shared; typo colliding with a built-in plugin name.","solutions":["Pick a unique name for the new plugin before calling registerPlugin","Call manager.hasPlugin(name) / listPlugins() first and only register when the name is absent","If replacing a built-in, use a different name or the API's designated override path instead of re-registering"],"exampleFix":"// before\nmanager.registerPlugin(\"metrics\", MyMetricsPlugin); // ValueError: built-in exists\n// after\nif (!manager.listPlugins().some(p => p.name === \"metrics\")) {\n  manager.registerPlugin(\"metrics\", MyMetricsPlugin);\n}","handlingStrategy":"validation","validationCode":"const taken = new Set(manager.listPlugins().map(p => p.name));\nif (!taken.has(name)) manager.registerPlugin(name, pluginClass);","typeGuard":null,"tryCatchPattern":"try {\n  manager.registerPlugin(name, pluginClass);\n} catch (err) {\n  if (err instanceof ValueError && err.message.includes(\"already registered\")) return; // idempotent re-init\n  throw err;\n}","preventionTips":["Check listPlugins()/registry before registering","Avoid re-running manager construction/initialization against a shared registry","Prefix custom plugin names to avoid colliding with built-ins (metrics, filter, compression)"],"tags":["plugin","duplicate-key","validation"],"backgroundTag":"duplicate-registration","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}