{"record":{"id":"accf5d4b27dd75ef","repo":"ruvnet/ruflo","slug":"invalid-plugin-does-not-implement-iplugin-interfa-accf5d","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/plugin-registry.ts","lineNumber":204,"sourceCode":"  }\n\n  // =========================================================================\n  // Plugin Loading\n  // =========================================================================\n\n  /**\n   * Register a plugin.\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\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    // Create config\n    const pluginConfig: PluginConfig = {\n      enabled: true,\n      priority: 50,","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/registry/plugin-registry.ts#L186-L222","documentation":"Thrown by the basic PluginRegistry.register() (the non-enhanced registry in plugins/src/registry/plugin-registry.ts) when validatePlugin(resolvedPlugin) rejects the object. Same contract as the enhanced registry: register() first awaits factory functions, then requires the resolved value to implement IPlugin (metadata with name/version, lifecycle methods); a partial object, wrong export, or bad factory result fails here before the duplicate-name and max-plugins checks.","triggerScenarios":"Passing a plain object without metadata or lifecycle methods; a plugin factory resolving to a partial or undefined value; importing a module namespace instead of the plugin instance (ESM/CJS interop); a plugin written for a different registry interface version (e.g. initialize() instead of init()).","commonSituations":"Custom plugin authoring that misses shutdown() or state; default/named import mistakes; bundlers transforming plugin modules so the default export is wrapped; plugin SDK upgrades renaming lifecycle methods while old plugins are loaded by the basic registry.","solutions":["Ensure the resolved object fully implements IPlugin: metadata.name, metadata.version, init(), shutdown(), state","If using a factory, return the complete plugin object; if using an instance, pass the instance directly","Check the import shape (module.default vs named) and register the instance your bundler actually produces","Match the lifecycle method names required by the current registry version"],"exampleFix":"// before\nimport * as MyPlugin from './my-plugin'; // namespace object, not the plugin\nawait registry.register(MyPlugin); // -> throws Invalid plugin\n\n// after\nimport { myPlugin } from './my-plugin'; // the IPlugin instance\nawait registry.register(myPlugin);","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(candidate);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('does not implement IPlugin')) {\n    logger.error('rejected plugin shape; keys:', Object.keys(candidate ?? {}));\n    return; // skip bad plugin, keep booting\n  }\n  throw err;\n}","preventionTips":["Type all plugin exports as IPlugin so the compiler enforces the contract","For dynamic plugin loading, run isIPlugin() on the resolved export and skip-with-log on failure","Re-check plugin bundles after SDK upgrades - lifecycle method renames surface here first"],"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"}