ruvnet/ruflo · error

Plugin ${name} not found

Error message

Plugin ${name} not found

What it means

Thrown by EnhancedPluginRegistry.unregister(name) as its first statement: a plain plugins.get(name) lookup that missed. Nothing else runs before it, so this error means exactly that no plugin with that name is registered on this registry instance (never registered, already unregistered, or name mismatch).

Source

Thrown at v3/@claude-flow/plugins/src/registry/enhanced-plugin-registry.ts:351

    // Store entry
    const entry: PluginEntry = {
      plugin: resolvedPlugin,
      config: pluginConfig,
      loadTime: new Date(),
    };

    this.plugins.set(name, entry);
    this.eventBus.emit(PLUGIN_EVENTS.LOADED, { plugin: name });
    this.logger.info(`Plugin registered: ${name} v${version}`);
  }

  /**
   * Unregister a plugin with dependency checking.
   */
  async unregister(name: string, options?: UnregisterOptions): Promise<void> {
    const entry = this.plugins.get(name);
    if (!entry) {
      throw new Error(`Plugin ${name} not found`);
    }

    // Check dependents
    const dependents = this.dependencyGraph.getDependents(name);

    if (dependents.length > 0) {
      if (options?.cascade) {
        // Unload dependents first (in reverse order)
        const order = this.dependencyGraph.getRemovalOrder(name);
        for (const dep of order) {
          if (dep !== name) {
            await this.shutdownPlugin(dep);
            this.removePluginFromGraph(dep);
          }
        }
      } else if (options?.force) {
        this.logger.warn(`Force removing ${name}, breaking: ${dependents.join(', ')}`);
      } else {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. List registered plugins (registry list API or logs) and use the exact metadata.name
  2. Make teardown idempotent: only unregister when the plugin is actually present
  3. Source the name from the plugin object's metadata rather than a duplicated string literal

Example fix

// before
await registry.unregister('logger-plugin'); // registered as 'logger' -> throws

// after
const registered = (await registry.list()).map(p => p.metadata.name);
if (registered.includes('logger')) {
  await registry.unregister('logger');
}
Defensive patterns

Strategy: validation

Validate before calling

const registered = new Set((await registry.list()).map(p => p.metadata.name));
if (!registered.has(name)) {
  logger.debug(`unregister skipped: '${name}' not registered`);
} else {
  await registry.unregister(name);
}

Try / catch

try {
  await registry.unregister(name);
} catch (err) {
  if (err instanceof Error && err.message.endsWith('not found')) return; // idempotent teardown
  throw err;
}

Prevention

When it happens

Trigger: Calling unregister('my-plugin') when the plugin registered under a different metadata.name; unregistering during shutdown after it was already unregistered (double-teardown); typo or casing differences between the registration name and the teardown call.

Common situations: Cleanup code in tests running against a fresh registry; shutdown handlers racing with a prior unregister; name drift between the plugin package's metadata and hard-coded teardown strings.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/fe7c820be8d1718f. Report an issue: GitHub.