agalwood/Motrix · error · AppError
PLUGIN_MANIFEST_INVALID
PLUGIN_MANIFEST_INVALID
Error message
plugin.install.invalid_github_spec
What it means
normalizeSource() rejects a github spec that does not match /^([^/@]+)\/([^/@]+)(?:@.+)?$/. The regex requires exactly 'owner/repo' optionally followed by '@tag'. Any missing slash, extra '@', whitespace, or empty segment fails the match and throws before a URL is ever built.
Source
Thrown at src/core/plugin/install/source-resolver.ts:45
| { type: 'volume'; containerPath: string }
| { type: 'env'; url: string }
| { type: 'builtin'; resourcePath: string }
// The registry — not the package host — is the trust anchor: the same
// plugin re-published from a different CDN keeps the same source URL, so
// upgrades via the registry never trip diff.sourceUrlChanged re-consent.
| { type: 'registry'; pluginId: string }
export interface NormalizedSource {
type: InstallSourceType
url: string
}
export function normalizeSource(s: SourceInput): NormalizedSource {
switch (s.type) {
case 'github': {
const m = s.spec.match(/^([^/@]+)\/([^/@]+)(?:@.+)?$/)
if (!m) {
throw new AppError(
ErrorCode.PluginManifestInvalid,
'plugin.install.invalid_github_spec'
)
}
const [, owner, repo] = m
return { type: 'github', url: `https://github.com/${owner}/${repo}` }
}
case 'url': {
let u: URL
try {
u = new URL(s.url)
} catch {
throw new AppError(
ErrorCode.PluginManifestInvalid,
'plugin.install.invalid_url'
)
}
if (u.protocol !== 'http:' && u.protocol !== 'https:') {View on GitHub (pinned to 1a708ee577)
Solutions
- Provide the spec in 'owner/repo' or 'owner/repo@tag' form.
- Strip any 'github:' scheme prefix before passing the spec to normalizeSource.
- Validate with the regex /^([^/@]+)\/([^/@]+)(?:@.+)?$/ before calling.
Example fix
// before
normalizeSource({type:'github', spec:'github:owner/repo@v1'})
// after — strip scheme, keep owner/repo@tag
normalizeSource({type:'github', spec:'owner/repo@v1'}) Defensive patterns
Strategy: validation
Validate before calling
function isValidGithubSpec(spec:string){
return /^([^/@]+)\/([^/@]+)(?:@.+)?$/.test(spec)
}
if(!isValidGithubSpec(spec)) throw new Error('expected owner/repo[@tag]')
normalizeSource({type:'github', spec}) Type guard
const isGithubSpec = (s:string): boolean => /^([^/@]+)\/([^/@]+)(?:@.+)?$/.test(s)
Try / catch
try { normalizeSource({type:'github', spec}) }
catch(e){ if(e instanceof AppError && e.message==='plugin.install.invalid_github_spec'){ /* prompt for owner/repo@tag */ } else throw e } Prevention
- Strip any 'github:' scheme prefix before passing the spec.
- Validate the spec with the regex before calling normalizeSource.
- Use a CLI flag parser that keeps 'owner/repo@tag' intact.
When it happens
Trigger: Passing {type:'github', spec:'owner'} (no slash), 'owner/repo@tag@extra', '/repo', 'owner/', or a spec with '@' in the owner segment.
Common situations: User typed 'github:owner' instead of 'github:owner/repo'; CLI arg parsing split 'owner/repo@v1' incorrectly; copy-paste included a leading 'github:' prefix into the spec field.
Related errors
- PLUGIN_MANIFEST_INVALID
- invalid-url-scheme
- invalid Chrome extension ID: ${id}
- invalid Firefox extension ID: ${id}
- aria2 addTorrent gid must contain exactly 16 hexadecimal cha
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/66343353886433b2.
Report an issue: GitHub.