{"record":{"id":"f0c6052a0d1b2349","repo":"agalwood/Motrix","slug":"invalid-url-scheme","errorCode":"invalid-url-scheme","errorMessage":"URL must be http: or https:","messagePattern":"URL must be http: or https:","errorType":"error_code","errorClass":"BridgeReceiverError","httpStatus":null,"severity":"error","filePath":"src/core/bridge-receiver/submit-download-adapter.ts","lineNumber":100,"sourceCode":"\n  async adapt(\n    params: DownloadSubmitParams,\n    input: AdaptInput\n  ): Promise<\n    AdaptedDirect | AdaptedMagnet | AdaptedHls | AdaptedDash | AdaptedMux\n  > {\n    // Bootstrap already ran DownloadSubmitParamsSchema.safeParse() and threw\n    // InvalidParams on failure. We have typed data here, but MDXP's\n    // Resource.url is plain z.string() (not http-only), so we still need to\n    // reject non-http(s) schemes as a business rule.\n    if (\n      params.selection.kind === 'direct' ||\n      params.selection.kind === 'hls' ||\n      params.selection.kind === 'dash'\n    ) {\n      const url = params.selection.primary.url\n      if (!/^https?:\\/\\//i.test(url)) {\n        throw new BridgeReceiverError(\n          'invalid-url-scheme',\n          'URL must be http: or https:'\n        )\n      }\n    }\n    if (params.selection.kind === 'mux') {\n      for (const r of [params.selection.video, params.selection.audio]) {\n        if (!/^https?:\\/\\//i.test(r.url)) {\n          throw new BridgeReceiverError(\n            'invalid-url-scheme',\n            'URL must be http: or https:'\n          )\n        }\n      }\n    }\n\n    const { selection, source, meta } = params\n","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/bridge-receiver/submit-download-adapter.ts#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate the URL scheme on the extension side before submitting — only submit http/https URLs for direct/hls/dash kinds","If the resource is actually a magnet link, submit it with selection.kind 'magnet' instead","Sanitize or reject blob:/data:/file: URLs at capture time in the extension"],"exampleFix":"// before\nawait adapter.adapt({ selection: { kind: 'direct', primary: { url: 'ftp://example.com/file.mp4', ... } }, ... }, input)\n// after\nawait adapter.adapt({ selection: { kind: 'direct', primary: { url: 'https://example.com/file.mp4', ... } }, ... }, input)","handlingStrategy":"validation","validationCode":"function isValidHttpUrl(url: string): boolean {\n  return /^https?:\\/\\//i.test(url)\n}\n// Before calling adapt:\nif (!isValidHttpUrl(params.selection.primary.url)) {\n  throw new Error(`Refusing to submit non-http URL: ${params.selection.primary.url}`)\n}","typeGuard":"function isHttpUrl(url: string): url is \\`http\\${string}\\` | \\`https\\${string}\\` {\n  return /^https?:\\/\\//i.test(url)\n}","tryCatchPattern":"try {\n  await adapter.adapt(params, input)\n} catch (e) {\n  if (e instanceof BridgeReceiverError && e.code === 'invalid-url-scheme') {\n    showError('Only http:// and https:// URLs are supported for this download type')\n  } else throw e\n}","preventionTips":["Validate URL scheme in the extension before submitting — filter out blob:, data:, file:, ftp: at capture time","Use magnet selection kind for magnet URIs instead of direct"],"tags":["validation","url","bridge-receiver","scheme"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}