{"record":{"id":"75136694e6dbd212","repo":"ruvnet/ruflo","slug":"cannot-disable-pluginname-other-plugins-may-de","errorCode":null,"errorMessage":"Cannot disable ${pluginName}: other plugins may depend on it","messagePattern":"Cannot disable (.+?): other plugins may depend on it","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/collections/collection-manager.ts","lineNumber":234,"sourceCode":"   * Disable a plugin from a collection.\n   */\n  async disablePlugin(collectionId: string, pluginName: string): Promise<void> {\n    const resolved = this.findResolvedEntry(collectionId, pluginName);\n    if (!resolved) {\n      throw new Error(`Plugin ${pluginName} not found in collection ${collectionId}`);\n    }\n\n    // Remove from enabled, add to disabled\n    this.enabledOverrides.get(collectionId)?.delete(pluginName);\n    this.disabledOverrides.get(collectionId)?.add(pluginName);\n\n    // Unregister if registered\n    if (this.registry.getPlugin(pluginName)) {\n      try {\n        await this.registry.unregister(pluginName);\n      } catch {\n        // May fail if other plugins depend on it\n        throw new Error(`Cannot disable ${pluginName}: other plugins may depend on it`);\n      }\n    }\n  }\n\n  /**\n   * Check if a plugin is enabled.\n   */\n  isEnabled(collectionId: string, pluginName: string): boolean {\n    const resolved = this.findResolvedEntry(collectionId, pluginName);\n    if (!resolved) return false;\n\n    return this.isPluginEnabledSync(collectionId, pluginName, resolved.entry.defaultEnabled);\n  }\n\n  /**\n   * Toggle a plugin's enabled state.\n   */\n  async togglePlugin(collectionId: string, pluginName: string): Promise<boolean> {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/collections/collection-manager.ts#L216-L252","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","solutions":["Identify plugins that depend on it (registry dependency data or collection manifests) and disable them first","For teardown, disable plugins in reverse dependency order","If the cause is unclear, call registry.unregister(pluginName) directly to surface the real error the manager swallowed"],"exampleFix":"// before\nawait manager.disablePlugin('analytics', 'auth'); // dependents still registered -> throws\n\n// after\nfor (const dep of dependentsOf('auth')) {\n  await manager.disablePlugin('analytics', dep);\n}\nawait manager.disablePlugin('analytics', 'auth');","handlingStrategy":"try-catch","validationCode":"// if the registry exposes dependency data, pre-check dependents:\nconst dependents = registry\n  .listPlugins()\n  .filter((p) => (p.dependencies ?? []).includes(pluginName))\n  .map((p) => p.name);\nif (dependents.length > 0) {\n  throw new Error(`disable dependents first: ${dependents.join(', ')}`);\n}\nawait manager.disablePlugin(collectionId, pluginName);","typeGuard":null,"tryCatchPattern":"try {\n  await manager.disablePlugin(collectionId, pluginName);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Cannot disable') && e.message.includes('depend on it')) {\n    // discover dependents via the registry, disable them first, then retry;\n    // for the true root cause call registry.unregister(pluginName) directly,\n    // since the manager swallows the original error\n  } else throw e;\n}","preventionTips":["Disable plugins in reverse dependency (topological) order","Document each plugin's dependents next to its enable/disable config","When diagnosing, bypass the manager and call the registry directly to see the real error"],"tags":["plugins","collection-manager","dependency-conflict","registry"],"backgroundTag":"plugin-dependency-conflict","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}