agalwood/Motrix · error · AppError
PLUGIN_MANIFEST_INVALID
PLUGIN_MANIFEST_INVALID
Error message
unknown plugin: ${pluginId} What it means
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.
Source
Thrown at src/core/plugin/host/plugin-host.ts:224
}
// Coalesce concurrent activations. `active` is only populated after the
// awaits in doActivate (ffmpeg detect, bundle read, permission
// resolution), so two overlapping calls would otherwise both pass the
// guard above, both spawn a worker, and the second active.set() would
// orphan the first bridge. Share one in-flight promise keyed by pluginId.
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 =View on GitHub (pinned to 1a708ee577)
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.
Example fix
// before
await host.activate('transcoder') // wrong id
// after
const ids = host.registry.list().map(p => p.id)
const id = ids.find(x => x.toLowerCase() === 'transcoder')
if (!id) throw new Error(`unknown plugin; installed: ${ids.join(', ')}`)
await host.activate(id) Defensive patterns
Strategy: validation
Validate before calling
function assertPluginKnown(pluginId: string, registry: { get(id: string): unknown; list(): Array<{ id: string }> }): void {
if (!registry.get(pluginId)) {
const known = registry.list().map(p => p.id).join(', ')
throw new Error(`unknown plugin ${pluginId}; installed: ${known}`)
}
} Type guard
function isInstalledPlugin(pluginId: string, registry: { get(id: string): unknown }): boolean {
return registry.get(pluginId) != null
} Try / catch
try {
await host.activate(pluginId)
} catch (e) {
if (e.code === 'PLUGIN_MANIFEST_INVALID' && /unknown plugin/.test(e.message)) {
// re-scan the registry, refresh the id list, and prompt the user to pick
} else throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- PluginActivationCapExceeded
- PLUGIN_RUNTIME_FAULT
- PLUGIN_ACTIVATION_CAP_EXCEEDED
- plugin.lifecycle.activation_capability_violation
- plugin locale commit and rollback both failed
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/3cc5fa8f9c1b1c91.
Report an issue: GitHub.