{"record":{"id":"0ed884d9ba0d0f88","repo":"ruvnet/ruflo","slug":"invalid-plugin-does-not-implement-iplugin-interfa","errorCode":null,"errorMessage":"Invalid plugin: does not implement IPlugin interface","messagePattern":"Invalid plugin: does not implement IPlugin interface","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts","lineNumber":284,"sourceCode":"  }\n\n  // =========================================================================\n  // Plugin Loading\n  // =========================================================================\n\n  /**\n   * Register a plugin with version constraint validation.\n   */\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)) {","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts#L266-L302","documentation":"Thrown by EnhancedPluginRegistry.register() when validatePlugin(resolvedPlugin) rejects the object. register() first resolves factory functions (plugin can be an async factory), then requires the result to satisfy the IPlugin shape - metadata (name, version), lifecycle methods (init/shutdown), state. An object or factory result missing those members fails validation before any name or version checks run.","triggerScenarios":"Passing a plain object literal that lacks metadata or lifecycle methods; passing a factory function that resolves to a partial plugin (e.g. returns { metadata } only); passing a class instead of an instance; a default-vs-named import mixup that hands over undefined or a module namespace object.","commonSituations":"ESM/CJS interop where the imported binding is a module namespace rather than the plugin instance; plugin package upgraded to a new API that renamed init to initialize; writing a custom plugin and forgetting shutdown(); factories that throw or return undefined under some conditions.","solutions":["Check the resolved value implements the full IPlugin interface: metadata.name, metadata.version, init(), shutdown(), state - before registering","If registering a factory, ensure it resolves (returns) the plugin object, not a class or partial object","Verify the import: use the instance/value export, not the module namespace (check for .default in transpiled CJS)","Align with the current interface version of the plugin SDK - method names like init/shutdown must match exactly"],"exampleFix":"// before\nregistry.register({ metadata: { name: 'x', version: '1.0.0' } }); // no lifecycle -> throws\n\n// after\nregistry.register({\n  metadata: { name: 'x', version: '1.0.0' },\n  state: 'registered',\n  async init(context) { this.state = 'initialized'; },\n  async shutdown() { this.state = 'shutdown'; },\n} as unknown as IPlugin);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isIPlugin(candidate: unknown): candidate is IPlugin {\n  if (typeof candidate !== 'object' || candidate === null) return false;\n  const p = candidate as Record<string, unknown>;\n  const meta = p.metadata as Record<string, unknown> | undefined;\n  return (\n    !!meta &&\n    typeof meta.name === 'string' &&\n    typeof meta.version === 'string' &&\n    typeof p.init === 'function' &&\n    typeof p.shutdown === 'function'\n  );\n}","tryCatchPattern":"try {\n  await registry.register(plugin);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('does not implement IPlugin')) {\n    throw new Error(\n      `plugin failed IPlugin shape check; keys: ${JSON.stringify(Object.keys(plugin ?? {}))}`\n    );\n  }\n  throw err;\n}","preventionTips":["Type plugin factories as PluginFactory and plugin objects as IPlugin so the compiler catches missing members","Unit-test each plugin against the IPlugin contract (metadata + init + shutdown) before shipping","Watch ESM/CJS interop: register the resolved instance, not a module namespace or class reference"],"tags":["plugin-validation","plugin-registry","interface-contract","typescript"],"backgroundTag":"plugin-validation-failed","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"}