agalwood/Motrix · error · BridgeReceiverError

invalid-url-scheme

invalid-url-scheme

Error message

URL must be http: or https:

What it means

Thrown by SubmitDownloadAdapter.adapt when a direct, hls, or dash selection provides a primary URL that does not begin with http:// or https:// (case-insensitive regex). MDXP's Resource.url is typed as plain z.string() (not URL-validated), so this is a business-rule guard that runs after schema parsing. Code 'invalid-url-scheme' is a wire-contract code the extension can branch on.

Source

Thrown at src/core/bridge-receiver/submit-download-adapter.ts:100

  async adapt(
    params: DownloadSubmitParams,
    input: AdaptInput
  ): Promise<
    AdaptedDirect | AdaptedMagnet | AdaptedHls | AdaptedDash | AdaptedMux
  > {
    // Bootstrap already ran DownloadSubmitParamsSchema.safeParse() and threw
    // InvalidParams on failure. We have typed data here, but MDXP's
    // Resource.url is plain z.string() (not http-only), so we still need to
    // reject non-http(s) schemes as a business rule.
    if (
      params.selection.kind === 'direct' ||
      params.selection.kind === 'hls' ||
      params.selection.kind === 'dash'
    ) {
      const url = params.selection.primary.url
      if (!/^https?:\/\//i.test(url)) {
        throw new BridgeReceiverError(
          'invalid-url-scheme',
          'URL must be http: or https:'
        )
      }
    }
    if (params.selection.kind === 'mux') {
      for (const r of [params.selection.video, params.selection.audio]) {
        if (!/^https?:\/\//i.test(r.url)) {
          throw new BridgeReceiverError(
            'invalid-url-scheme',
            'URL must be http: or https:'
          )
        }
      }
    }

    const { selection, source, meta } = params

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Validate the URL scheme on the extension side before submitting — only submit http/https URLs for direct/hls/dash kinds
  2. If the resource is actually a magnet link, submit it with selection.kind 'magnet' instead
  3. Sanitize or reject blob:/data:/file: URLs at capture time in the extension

Example fix

// before
await adapter.adapt({ selection: { kind: 'direct', primary: { url: 'ftp://example.com/file.mp4', ... } }, ... }, input)
// after
await adapter.adapt({ selection: { kind: 'direct', primary: { url: 'https://example.com/file.mp4', ... } }, ... }, input)
Defensive patterns

Strategy: validation

Validate before calling

function isValidHttpUrl(url: string): boolean {
  return /^https?:\/\//i.test(url)
}
// Before calling adapt:
if (!isValidHttpUrl(params.selection.primary.url)) {
  throw new Error(`Refusing to submit non-http URL: ${params.selection.primary.url}`)
}

Type guard

function isHttpUrl(url: string): url is \`http\${string}\` | \`https\${string}\` {
  return /^https?:\/\//i.test(url)
}

Try / catch

try {
  await adapter.adapt(params, input)
} catch (e) {
  if (e instanceof BridgeReceiverError && e.code === 'invalid-url-scheme') {
    showError('Only http:// and https:// URLs are supported for this download type')
  } else throw e
}

Prevention

When it happens

Trigger: params.selection.kind is 'direct', 'hls', or 'dash' and params.selection.primary.url fails the regex /^https?:\/\//i — e.g. it starts with 'ftp://', 'file://', 'magnet:', 'data:', 'javascript:', or is a relative path.

Common situations: Extension captures a non-http media link (ftp download, blob URL); a malformed or truncated URL is passed; a magnet URI is incorrectly classified as 'direct'; a data: URI for an inline media element is captured.

Related errors


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