agalwood/Motrix · error · AppError

PLUGIN_RUNTIME_FAULT

PLUGIN_RUNTIME_FAULT

Error message

plugin ${pluginId} is disabled

What it means

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.

Source

Thrown at src/core/plugin/host/plugin-host.ts:230

    const inFlight = this.activating.get(pluginId)
    if (inFlight) return inFlight
    const promise = this.doActivate(pluginId).finally(() => {
      this.activating.delete(pluginId)
    })
    this.activating.set(pluginId, promise)
    return promise
  }

  private async doActivate(pluginId: string): Promise<void> {
    const indexed = this.opts.registry.get(pluginId)
    if (!indexed) {
      throw new AppError(
        ErrorCode.PluginManifestInvalid,
        `unknown plugin: ${pluginId}`
      )
    }
    if (!indexed.state.enabled) {
      throw new AppError(
        ErrorCode.PluginRuntimeFault,
        `plugin ${pluginId} is disabled`
      )
    }
    if (this.active.size >= this.maxActivePlugins) {
      throw new AppError(
        ErrorCode.PluginActivationCapExceeded,
        `active plugin cap (${this.maxActivePlugins}) reached`
      )
    }

    const needsFfmpeg =
      indexed.manifest.permissions.includes('ffmpeg') ||
      (indexed.manifest.optionalPermissions ?? []).includes('ffmpeg')

    let ffmpegDetection: FfmpegDetection = { available: false }
    if (needsFfmpeg && this.opts.ffmpegDetect) {
      ffmpegDetection = await this.opts.ffmpegDetect()

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Check indexed.state.enabled before activating and prompt the user to enable if false.
  2. Inspect stateStore/registry for the recorded error that caused the auto-disable and fix the root cause first.
  3. Call the registry enable path (re-grant consent if required) before retrying activate.

Example fix

// before
await host.activate(id) // throws if disabled
// after
const st = host.registry.get(id)
if (!st?.state.enabled) {
  await host.registry.enable(id) // may require consent
}
await host.activate(id)
Defensive patterns

Strategy: validation

Validate before calling

function assertPluginEnabled(pluginId: string, registry: { get(id: string): { state: { enabled: boolean } } | undefined }): void {
  const entry = registry.get(pluginId)
  if (!entry?.state.enabled) {
    throw new Error(`plugin ${pluginId} is disabled; enable it (and grant consent) before activating`)
  }
}

Type guard

function isPluginEnabled(entry: { state: { enabled: boolean } } | undefined): entry is { state: { enabled: true } } {
  return !!entry && entry.state.enabled === true
}

Try / catch

try {
  await host.activate(pluginId)
} catch (e) {
  if (e.code === 'PLUGIN_RUNTIME_FAULT' && /disabled/.test(e.message)) {
    // inspect stateStore for the recorded error, fix root cause, then enable + retry
  } else throw e
}

Prevention

When it happens

Trigger: User disabled the plugin; the host auto-disabled it after a prior error (stateStore.recordError + refreshState); consent/grant was never given; policy disabled it.

Common situations: Prior activation/ffmpeg/signature error auto-disabled the plugin; user toggled it off in UI; consent flow not completed; grants revoked.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/88f82cf9d4e711b5. Report an issue: GitHub.