{"record":{"id":"21d7cec68720d4bc","repo":"can1357/oh-my-pi","slug":"destination-returned-an-unsupported-upload-url","errorCode":null,"errorMessage":"${destination} returned an unsupported upload URL","messagePattern":"(.+?) returned an unsupported upload URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/uploaders-anonymous.ts","lineNumber":37,"sourceCode":"const DAY_MS = 24 * HOUR_MS;\n\nconst LITTERBOX_TTLS = {\n\t\"1h\": HOUR_MS,\n\t\"12h\": 12 * HOUR_MS,\n\t\"24h\": 24 * HOUR_MS,\n\t\"72h\": 72 * HOUR_MS,\n} as const;\n\nfunction httpUrl(value: string, destination: BlobDestinationId): string {\n\tconst trimmed = value.trim();\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(trimmed);\n\t} catch {\n\t\tthrow new Error(`${destination} returned an invalid upload URL`);\n\t}\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(`${destination} returned an unsupported upload URL`);\n\t}\n\treturn url.href;\n}\n\nasync function uploadTextUrl(\n\tdestination: BlobDestinationId,\n\tconfig: DestinationRuntimeConfig,\n\tendpoint: string,\n\tform: FormData,\n): Promise<{ response: Response; url: string }> {\n\tconst response = await expectOk(await fetchFor(config)(endpoint, { method: \"POST\", body: form }), destination);\n\treturn { response, url: httpUrl(await response.text(), destination) };\n}\n\nfunction remoteName(url: string): string | undefined {\n\tconst name = new URL(url).pathname.split(\"/\").filter(Boolean).pop();\n\treturn name ? decodeURIComponent(name) : undefined;\n}","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/uploaders-anonymous.ts#L19-L55","documentation":"httpUrl throws this when the value parses as a URL but its protocol is neither http: nor https:. The library only uploads over HTTP(S); schemes like ftp:, file:, data:, or javascript: are rejected to prevent unsafe or unsupported requests.","triggerScenarios":"Calling httpUrl with a configured or host-returned URL whose scheme is not http/https — e.g. `file:///tmp/upload`, `ftp://host/path`, or a `data:` URI in the base/uploadUrl option or in a response field passed to uploadTextUrl.","commonSituations":"Local-file-scheme URLs pasted from documentation examples, custom internal schemes from a corporate proxy, or a malicious/compromised host returning a data: URL to redirect uploads.","solutions":["Change the option to an http:// or https:// URL.","If the destination is local-only, upload it directly rather than through this HTTP uploader.","If a host legitimately returns non-HTTP URLs, report/switch destinations — the library will not follow them by design."],"exampleFix":"// before\n{ \"options\": { \"uploadUrl\": \"file:///srv/uploads\" } }\n// after\n{ \"options\": { \"uploadUrl\": \"https://example.com/upload\" } }","handlingStrategy":"validation","validationCode":"function assertHttpScheme(v: unknown): void {\n  const u = typeof v === \"string\" ? (() => { try { return new URL(v.trim()); } catch { return null; } })() : null;\n  if (!u || (u.protocol !== \"http:\" && u.protocol !== \"https:\")) throw new Error(`URL scheme must be http(s), got: ${u?.protocol ?? v}`);\n}\nassertHttpScheme(config.options.uploadUrl);","typeGuard":"const isHttpUrl = (v: string): boolean => { try { const u = new URL(v.trim()); return u.protocol === \"http:\" || u.protocol === \"https:\"; } catch { return false; } };","tryCatchPattern":"try {\n  uploader = createUploader(config);\n} catch (err) {\n  if (String(err).includes(\"unsupported upload URL\")) throw new Error(\"only http/https upload URLs are supported\");\n  throw err;\n}","preventionTips":["Reject file:, ftp:, and data: schemes in your own config validation before creating the uploader.","Sanitize host-returned URLs: never pass a response-provided URL through if its scheme is not http(s).","Treat non-HTTP schemes returned by a remote as a compromise signal."],"tags":["config","url","security"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}