mihomo-party-org/clash-party · error
Invalid plugin URL
Error message
Invalid plugin URL
What it means
parseDownloadUrl wraps the standard URL constructor: any input that fails new URL(url) is rethrown as 'Invalid plugin URL'. This is a syntax-level rejection before the https/userinfo/fragment/host checks run.
Source
Thrown at src/main/resolve/plugin/remote.ts:11
import { getAppConfig } from '../../config/app'
import { MAX_PLUGIN_FILE_BYTES } from './constants'
import { requestOnce } from './http-client'
import { createGuardedLookup, isForbiddenHost } from './net-guard'
function parseDownloadUrl(url: string): URL {
let parsed: URL
try {
parsed = new URL(url)
} catch {
throw new Error('Invalid plugin URL')
}
if (parsed.protocol !== 'https:') throw new Error('Plugin URL must use https')
if (parsed.username || parsed.password) throw new Error('Plugin URL must not contain userinfo')
if (parsed.hash) throw new Error('Plugin URL must not contain a fragment')
if (isForbiddenHost(parsed.hostname)) throw new Error('Plugin URL must use a public host')
return parsed
}
export async function fetchRemotePlugin(url: string): Promise<string> {
const parsed = parseDownloadUrl(url)
const { subscriptionTimeout = 30000, pluginUseProxy } = await getAppConfig()
let proxy: { host: string; port: number } | undefined
if (pluginUseProxy) {
const { getControledMihomoConfig } = await import('../../config/controledMihomo')
const { 'mixed-port': port = 7890 } = await getControledMihomoConfig()
proxy = { host: '127.0.0.1', port }
}
View on GitHub (pinned to 911e090537)
Solutions
- Prepend the scheme if missing (e.g. turn 'example.com/p.yaml' into 'https://example.com/p.yaml') before calling.
- Validate with new URL(url) in the caller and surface a user-friendly message.
- Ensure special characters are URL-encoded (spaces -> %20).
- Remember only absolute https URLs are accepted — local files are not valid download URLs.
Example fix
// before
await fetchRemotePlugin('example.com/plugins/app.yaml') // throws
// after
await fetchRemotePlugin('https://example.com/plugins/app.yaml') Defensive patterns
Strategy: validation
Validate before calling
let u: URL
try { u = new URL(input) } catch { throw new Error('enter a valid absolute URL') }
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(input)) throw new Error('URL must include a scheme, e.g. https://') Type guard
const isAbsoluteUrl = (s: string): boolean => {
try { new URL(s); return true } catch { return false }
} Try / catch
try {
await fetchRemotePlugin(input)
} catch (e) {
if (e.message === 'Invalid plugin URL') {
showUrlInputError('Not a valid absolute URL')
} else throw e
} Prevention
- Trim and scheme-check user-supplied URLs before calling
- Require users to paste full absolute https URLs
- URL-encode spaces and special characters
- Reject local file paths at the input layer
When it happens
Trigger: Passing a non-URL string to fetchRemotePlugin / parseDownloadUrl — e.g. 'myplugin/download', an empty string, a file path like '/opt/plugins/x.yaml', or a URL with illegal characters (unencoded spaces, stray brackets).
Common situations: User pastes a relative path or bare domain without scheme; clipboard copy dropped part of the URL; local file path given where an https download URL is required.
Related errors
- Plugin URL must not contain a fragment
- Unsupported plugin file type
- Plugin path is not a file
- Plugin file too large
- Plugin URL must use https
AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30).
Data as JSON: /api/errors/4a2f085c3e52e2ff.
Report an issue: GitHub.