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

  1. 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).
  2. In tests, use a real semver triple for hostVersion rather than a placeholder.
  3. 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

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


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