ruvnet/ruflo · error · Error

Cannot disable ${pluginName}: other plugins may depend on it

Error message

Cannot disable ${pluginName}: other plugins may depend on it

What it means

disablePlugin flips the enabled/disabled override sets first, then calls registry.unregister(pluginName) inside try/catch; any registry failure — in practice, other registered plugins depend on this one — is swallowed and rethrown as this generic message. Note two consequences: the original cause is hidden, and the disabled override has already been recorded even though the plugin remains registered.

Source

Thrown at v3/@claude-flow/plugins/src/collections/collection-manager.ts:234

   * Disable a plugin from a collection.
   */
  async disablePlugin(collectionId: string, pluginName: string): Promise<void> {
    const resolved = this.findResolvedEntry(collectionId, pluginName);
    if (!resolved) {
      throw new Error(`Plugin ${pluginName} not found in collection ${collectionId}`);
    }

    // Remove from enabled, add to disabled
    this.enabledOverrides.get(collectionId)?.delete(pluginName);
    this.disabledOverrides.get(collectionId)?.add(pluginName);

    // Unregister if registered
    if (this.registry.getPlugin(pluginName)) {
      try {
        await this.registry.unregister(pluginName);
      } catch {
        // May fail if other plugins depend on it
        throw new Error(`Cannot disable ${pluginName}: other plugins may depend on it`);
      }
    }
  }

  /**
   * Check if a plugin is enabled.
   */
  isEnabled(collectionId: string, pluginName: string): boolean {
    const resolved = this.findResolvedEntry(collectionId, pluginName);
    if (!resolved) return false;

    return this.isPluginEnabledSync(collectionId, pluginName, resolved.entry.defaultEnabled);
  }

  /**
   * Toggle a plugin's enabled state.
   */
  async togglePlugin(collectionId: string, pluginName: string): Promise<boolean> {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Identify plugins that depend on it (registry dependency data or collection manifests) and disable them first
  2. For teardown, disable plugins in reverse dependency order
  3. If the cause is unclear, call registry.unregister(pluginName) directly to surface the real error the manager swallowed

Example fix

// before
await manager.disablePlugin('analytics', 'auth'); // dependents still registered -> throws

// after
for (const dep of dependentsOf('auth')) {
  await manager.disablePlugin('analytics', dep);
}
await manager.disablePlugin('analytics', 'auth');
Defensive patterns

Strategy: try-catch

Validate before calling

// if the registry exposes dependency data, pre-check dependents:
const dependents = registry
  .listPlugins()
  .filter((p) => (p.dependencies ?? []).includes(pluginName))
  .map((p) => p.name);
if (dependents.length > 0) {
  throw new Error(`disable dependents first: ${dependents.join(', ')}`);
}
await manager.disablePlugin(collectionId, pluginName);

Try / catch

try {
  await manager.disablePlugin(collectionId, pluginName);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Cannot disable') && e.message.includes('depend on it')) {
    // discover dependents via the registry, disable them first, then retry;
    // for the true root cause call registry.unregister(pluginName) directly,
    // since the manager swallows the original error
  } else throw e;
}

Prevention

When it happens

Trigger: Disabling a plugin that other registered plugins list as a dependency; any registry.unregister failure (the underlying error is masked, hence the hedged 'may depend on it').

Common situations: Disabling shared infrastructure plugins (auth, telemetry) that a collection's other plugins depend on; teardown ordering issues in plugin dependency graphs; registry state problems producing unregister errors.

Related errors


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