agalwood/Motrix · error · PluginManifestInvalid
PLUGIN_MANIFEST_INVALID
PLUGIN_MANIFEST_INVALID
Error message
unparseable host version: ${s} What it means
parseVer() is the internal helper behind semverSatisfies(); it expects a major.minor.patch prefix matching /^(\d+)\.(\d+)\.(\d+)/. It is called on the HOST version (opts.hostVersion), not the plugin's. If the host version string lacks that numeric triple, parsing aborts — this is a host/app misconfiguration, not a plugin author error.
Source
Thrown at src/core/plugin/manifest/parse.ts:63
export interface ParseResult {
manifest: ManifestZodOutput
warnings: ManifestWarning[]
}
export function semverSatisfies(version: string, range: string): boolean {
if (range.trim() === '*') return true
const v = parseVer(version)
const clauses = range.trim().split(/\s+/)
for (const c of clauses) {
if (!testClause(v, c)) return false
}
return true
}
function parseVer(s: string): [number, number, number] {
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(s)
if (!m)
throw new PluginManifestInvalid(
'plugin.manifest.host_version_unparseable',
`unparseable host version: ${s}`
)
return [Number(m[1]), Number(m[2]), Number(m[3])]
}
function testClause(v: [number, number, number], clause: string): boolean {
const m = /^(>=|<=|>|<|=|\^|~)?(\d+)\.(\d+)\.(\d+)/.exec(clause)
if (!m)
throw new PluginManifestInvalid(
'plugin.manifest.engines_unparseable',
`unparseable range clause: ${clause}`
)
const op = m[1] ?? '='
const r: [number, number, number] = [Number(m[2]), Number(m[3]), Number(m[4])]
const cmp = cmpVer(v, r)
switch (op) {
case '>=':View on GitHub (pinned to 1a708ee577)
Solutions
- Pass a hostVersion that starts with numeric major.minor.patch, e.g. '2.5.0' (a pre-release suffix like '-beta' is fine since only the prefix is parsed).
- In tests, use a real semver triple for hostVersion rather than a placeholder.
- Ensure the app's build pipeline emits a semver-shaped version into the value passed as hostVersion.
Example fix
// before
parseManifest(raw, {hostVersion: 'v2-latest'})
// after
parseManifest(raw, {hostVersion: '2.5.0'}) Defensive patterns
Strategy: validation
Validate before calling
function assertSemverPrefix(s:string){ if(!/^(\d+)\.(\d+)\.(\d+)/.test(s)) throw new Error(`hostVersion "${s}" unparseable`) }
assertSemverPrefix(hostVersion)
parseManifest(raw, {hostVersion}) Type guard
const hasSemverPrefix = (s:string): boolean => /^(\d+)\.(\d+)\.(\d+)/.test(s)
Try / catch
try { parseManifest(raw, {hostVersion}) }
catch(e){ if(e instanceof PluginManifestInvalid && e.validationCode==='plugin.manifest.host_version_unparseable'){ /* fix the host version string */ } else throw e } Prevention
- Always pass a numeric major.minor.patch hostVersion (a pre-release suffix is fine).
- Do not feed git SHAs, 'latest', or '0.0.0' into hostVersion.
- Centralize the app version stamp in the build pipeline and assert its shape.
When it happens
Trigger: Calling parseManifest(raw, {hostVersion}) with a hostVersion like 'v2', '2.x', '2.0.0-beta', empty string, or a git SHA. semverSatisfies invokes parseVer on the host version when engines.motrix is declared.
Common situations: Host app built with a non-semver version stamp (e.g. injected '0.0.0-dev' or a commit hash); test fixture passed hostVersion='latest'; build pipeline set VERSION to a git describe string with no numeric triple.
Related errors
- PLUGIN_ENGINE_VERSION_TOO_OLD
- transient-failure
- plugin.command.not_declared_in_manifest
- PluginManifestInvalid
- plugin.manifest.role.requires_builtin
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/bd6756f8f522782e.
Report an issue: GitHub.