agalwood/Motrix · error · AppError
PLUGIN_MANIFEST_INVALID
PLUGIN_MANIFEST_INVALID
Error message
plugin.install.invalid_github_spec
What it means
parseGithubSpec requires the form owner/repo or owner/repo@tag (regex ^([^/@\s]+)/([^/@\s]+)(?:@(.+))?$). A spec with whitespace, a missing slash, a leading @, extra path segments, or an unstripped 'github:' prefix does not match and throws PLUGIN_MANIFEST_INVALID.
Source
Thrown at src/core/plugin/install/github-fetcher.ts:21
//
// We don't read the manifest here — that happens later in `extractMoext`.
// We just provide a clean tarball-style "bring me the bytes" boundary.
import { mkdir, rm, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { AppError, ErrorCode } from '@shared/errors'
import { Agent, interceptors, request } from 'undici'
export interface GhReleaseSpec {
owner: string
repo: string
tag?: string
}
export function parseGithubSpec(spec: string): GhReleaseSpec {
const m = spec.match(/^([^/@\s]+)\/([^/@\s]+)(?:@(.+))?$/)
if (!m) {
throw new AppError(
ErrorCode.PluginManifestInvalid,
'plugin.install.invalid_github_spec'
)
}
const [, owner, repo, tag] = m
return tag ? { owner, repo, tag } : { owner, repo }
}
interface GhReleaseResponse {
tag_name: string
assets: Array<{ name: string; browser_download_url: string }>
}
export interface DownloadResult {
tag: string
assetName: string
}
View on GitHub (pinned to 1a708ee577)
Solutions
- Pass the spec as owner/repo or owner/repo@tag with no prefix and no whitespace.
- Strip a leading 'github:' and trim whitespace before calling parseGithubSpec.
- Validate the spec with the same regex before submitting it to the installer.
Example fix
// before
parseGithubSpec('github:owner/repo@v1.0') // no match
// after
const spec = raw.replace(/^github:/, '').trim()
parseGithubSpec(spec) // 'owner/repo@v1.0' Defensive patterns
Strategy: validation
Validate before calling
const GITHUB_SPEC_RE = /^([^/@\s]+)\/([^/@\s]+)(?:@(.+))?$/
function normalizeGithubSpec(raw: string): string {
const spec = raw.replace(/^github:/, '').trim()
if (!GITHUB_SPEC_RE.test(spec)) {
throw new Error(`invalid github spec: ${raw} (expected owner/repo[@tag])`)
}
return spec
} Type guard
function isValidGithubSpec(raw: string): boolean {
return /^([^/@\s]+)\/([^/@\s]+)(?:@(.+))?$/.test(raw.replace(/^github:/, '').trim())
} Try / catch
try {
parseGithubSpec(spec)
} catch (e) {
if (e.code === 'PLUGIN_MANIFEST_INVALID' && /invalid_github_spec/.test(e.message)) {
// prompt the user for owner/repo[@tag]; strip 'github:' and whitespace before retrying
} else throw e
} Prevention
- Strip a leading 'github:' and trim whitespace before parsing.
- Validate with the owner/repo[@tag] regex before submitting to the installer.
- Reject full URLs and multi-segment paths at the input boundary.
When it happens
Trigger: Caller passes 'foo' (no slash), 'foo/bar/baz', ' owner/repo' (leading space), '@owner/repo', or 'github:owner/repo' without stripping the prefix.
Common situations: User pastes a full GitHub URL or the 'github:' shorthand into the install field; whitespace from copy-paste; extra trailing slash or path.
Related errors
- PLUGIN_MANIFEST_INVALID
- PluginRuntimeFault
- PluginManifestInvalid
- plugin.capability.bad_args
- PLUGIN_MANIFEST_INVALID
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/e8c3a3c64bbfea18.
Report an issue: GitHub.