ruvnet/ruflo · error · PluginError

INVALID_PLUGIN

INVALID_PLUGIN

Error message

Plugin '${pluginName}' not found

What it means

PluginLoader.unloadPlugin(pluginName) looks the name up in the plugin registry and throws PluginError with code 'INVALID_PLUGIN' when nothing is registered under that exact name. Note the code is misleading (the condition is 'not found', not 'invalid') - branch on the message or pluginName, not just the code. Names are case-sensitive; loaded names are visible via loader.getInitializationOrder().

Source

Thrown at v3/@claude-flow/shared/src/plugin-loader.ts:185

    } finally {
      results.totalDuration = Date.now() - startTime;
    }

    // Start health checks if enabled
    if (this.config.enableHealthChecks) {
      this.startHealthChecks();
    }

    return results;
  }

  /**
   * Unload a single plugin
   */
  async unloadPlugin(pluginName: string): Promise<void> {
    const pluginInfo = this.registry.getPlugin(pluginName);
    if (!pluginInfo) {
      throw new PluginError(
        `Plugin '${pluginName}' not found`,
        pluginName,
        'INVALID_PLUGIN'
      );
    }

    // Check for dependents
    const dependents = this.findDependents(pluginName);
    if (dependents.length > 0) {
      throw new PluginError(
        `Cannot unload plugin '${pluginName}': depended on by ${dependents.join(', ')}`,
        pluginName,
        'DEPENDENCY_NOT_FOUND'
      );
    }

    // Shutdown plugin
    await this.shutdownPlugin(pluginInfo.plugin);

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Use the exact registered name - list them first via loader.getInitializationOrder()
  2. Guard with a presence check before unloading
  3. In unload-then-reload flows, ensure the original load completed before unloading

Example fix

// before
await loader.unloadPlugin('auth'); // actual name is 'auth-plugin'

// after
const loaded = loader.getInitializationOrder();
if (loaded.includes('auth-plugin')) {
  await loader.unloadPlugin('auth-plugin');
}
Defensive patterns

Strategy: validation

Validate before calling

const loadedNames = loader.getInitializationOrder();
if (!loadedNames.includes(pluginName)) {
  throw new Error(`plugin '${pluginName}' is not loaded; loaded: ${loadedNames.join(', ')}`);
}
await loader.unloadPlugin(pluginName);

Try / catch

try {
  await loader.unloadPlugin(name);
} catch (e) {
  if (e instanceof PluginError && e.code === 'INVALID_PLUGIN' && /not found/.test(e.message)) {
    return; // already gone - idempotent unload
  }
  throw e;
}

Prevention

When it happens

Trigger: unloadPlugin('auth') when the plugin registered as 'AuthPlugin'; unloading before loadPlugins resolved; a typo in a config-driven unload list; calling unload twice where the first already removed it.

Common situations: Config lists using stale names after a plugin rename; case mismatches; hot-unload flows racing the initial load; teardown code written against docs instead of the actual registered names.

Related errors


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