{"record":{"id":"3cc5fa8f9c1b1c91","repo":"agalwood/Motrix","slug":"plugin-manifest-invalid","errorCode":"PLUGIN_MANIFEST_INVALID","errorMessage":"unknown plugin: ${pluginId}","messagePattern":"unknown plugin: (.+?)","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/host/plugin-host.ts","lineNumber":224,"sourceCode":"    }\n    // Coalesce concurrent activations. `active` is only populated after the\n    // awaits in doActivate (ffmpeg detect, bundle read, permission\n    // resolution), so two overlapping calls would otherwise both pass the\n    // guard above, both spawn a worker, and the second active.set() would\n    // orphan the first bridge. Share one in-flight promise keyed by pluginId.\n    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 =","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/host/plugin-host.ts#L206-L242","documentation":"doActivate looks up pluginId in the registry; if registry.get returns nothing, it throws PLUGIN_MANIFEST_INVALID. The id was never installed/indexed, was removed, or does not match an installed plugin's id.","triggerScenarios":"activate('foo') where 'foo' is not in the registry; id casing or whitespace mismatch; the plugin was uninstalled before activation resolved; caller passed an id from a stale list.","commonSituations":"Caller uses a pluginId from a cached/stale list; uninstall race; typo or copy-paste of the id; id sourced from a config that references a plugin no longer installed.","solutions":["Look the plugin up via registry.get(id) (or listInstalled) before calling activate, and surface the available ids.","Re-scan/reload the registry if the id should exist.","Strip whitespace and match id casing exactly against the installed plugin id."],"exampleFix":"// before\nawait host.activate('transcoder') // wrong id\n// after\nconst ids = host.registry.list().map(p => p.id)\nconst id = ids.find(x => x.toLowerCase() === 'transcoder')\nif (!id) throw new Error(`unknown plugin; installed: ${ids.join(', ')}`)\nawait host.activate(id)","handlingStrategy":"validation","validationCode":"function assertPluginKnown(pluginId: string, registry: { get(id: string): unknown; list(): Array<{ id: string }> }): void {\n  if (!registry.get(pluginId)) {\n    const known = registry.list().map(p => p.id).join(', ')\n    throw new Error(`unknown plugin ${pluginId}; installed: ${known}`)\n  }\n}","typeGuard":"function isInstalledPlugin(pluginId: string, registry: { get(id: string): unknown }): boolean {\n  return registry.get(pluginId) != null\n}","tryCatchPattern":"try {\n  await host.activate(pluginId)\n} catch (e) {\n  if (e.code === 'PLUGIN_MANIFEST_INVALID' && /unknown plugin/.test(e.message)) {\n    // re-scan the registry, refresh the id list, and prompt the user to pick\n  } else throw e\n}","preventionTips":["Always source the pluginId from a fresh registry listing, not a cached value.","Re-scan/reload the registry before activating if installs may have changed.","Match id casing and strip whitespace before activating."],"tags":["plugin","activation","registry"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}