{"record":{"id":"88f82cf9d4e711b5","repo":"agalwood/Motrix","slug":"plugin-runtime-fault","errorCode":"PLUGIN_RUNTIME_FAULT","errorMessage":"plugin ${pluginId} is disabled","messagePattern":"plugin (.+?) is disabled","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/host/plugin-host.ts","lineNumber":230,"sourceCode":"    const inFlight = this.activating.get(pluginId)\n    if (inFlight) return inFlight\n    const promise = this.doActivate(pluginId).finally(() => {\n      this.activating.delete(pluginId)\n    })\n    this.activating.set(pluginId, promise)\n    return promise\n  }\n\n  private async doActivate(pluginId: string): Promise<void> {\n    const indexed = this.opts.registry.get(pluginId)\n    if (!indexed) {\n      throw new AppError(\n        ErrorCode.PluginManifestInvalid,\n        `unknown plugin: ${pluginId}`\n      )\n    }\n    if (!indexed.state.enabled) {\n      throw new AppError(\n        ErrorCode.PluginRuntimeFault,\n        `plugin ${pluginId} is disabled`\n      )\n    }\n    if (this.active.size >= this.maxActivePlugins) {\n      throw new AppError(\n        ErrorCode.PluginActivationCapExceeded,\n        `active plugin cap (${this.maxActivePlugins}) reached`\n      )\n    }\n\n    const needsFfmpeg =\n      indexed.manifest.permissions.includes('ffmpeg') ||\n      (indexed.manifest.optionalPermissions ?? []).includes('ffmpeg')\n\n    let ffmpegDetection: FfmpegDetection = { available: false }\n    if (needsFfmpeg && this.opts.ffmpegDetect) {\n      ffmpegDetection = await this.opts.ffmpegDetect()","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/host/plugin-host.ts#L212-L248","documentation":"The plugin is indexed in the registry but its state.enabled flag is false, so activation is refused with PLUGIN_RUNTIME_FAULT. Disabled plugins cannot be activated until re-enabled.","triggerScenarios":"User disabled the plugin; the host auto-disabled it after a prior error (stateStore.recordError + refreshState); consent/grant was never given; policy disabled it.","commonSituations":"Prior activation/ffmpeg/signature error auto-disabled the plugin; user toggled it off in UI; consent flow not completed; grants revoked.","solutions":["Check indexed.state.enabled before activating and prompt the user to enable if false.","Inspect stateStore/registry for the recorded error that caused the auto-disable and fix the root cause first.","Call the registry enable path (re-grant consent if required) before retrying activate."],"exampleFix":"// before\nawait host.activate(id) // throws if disabled\n// after\nconst st = host.registry.get(id)\nif (!st?.state.enabled) {\n  await host.registry.enable(id) // may require consent\n}\nawait host.activate(id)","handlingStrategy":"validation","validationCode":"function assertPluginEnabled(pluginId: string, registry: { get(id: string): { state: { enabled: boolean } } | undefined }): void {\n  const entry = registry.get(pluginId)\n  if (!entry?.state.enabled) {\n    throw new Error(`plugin ${pluginId} is disabled; enable it (and grant consent) before activating`)\n  }\n}","typeGuard":"function isPluginEnabled(entry: { state: { enabled: boolean } } | undefined): entry is { state: { enabled: true } } {\n  return !!entry && entry.state.enabled === true\n}","tryCatchPattern":"try {\n  await host.activate(pluginId)\n} catch (e) {\n  if (e.code === 'PLUGIN_RUNTIME_FAULT' && /disabled/.test(e.message)) {\n    // inspect stateStore for the recorded error, fix root cause, then enable + retry\n  } else throw e\n}","preventionTips":["Check state.enabled before activating and prompt for consent/enable.","Inspect recorded errors that auto-disabled the plugin and fix the root cause first.","Do not retry activate in a tight loop after a disable; re-enable explicitly."],"tags":["plugin","activation","state"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}