can1357/oh-my-pi · error
${destination} returned an unsupported upload URL
Error message
${destination} returned an unsupported upload URL What it means
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.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-anonymous.ts:37
const DAY_MS = 24 * HOUR_MS;
const LITTERBOX_TTLS = {
"1h": HOUR_MS,
"12h": 12 * HOUR_MS,
"24h": 24 * HOUR_MS,
"72h": 72 * HOUR_MS,
} as const;
function httpUrl(value: string, destination: BlobDestinationId): string {
const trimmed = value.trim();
let url: URL;
try {
url = new URL(trimmed);
} catch {
throw new Error(`${destination} returned an invalid upload URL`);
}
if (url.protocol !== "http:" && url.protocol !== "https:") {
throw new Error(`${destination} returned an unsupported upload URL`);
}
return url.href;
}
async function uploadTextUrl(
destination: BlobDestinationId,
config: DestinationRuntimeConfig,
endpoint: string,
form: FormData,
): Promise<{ response: Response; url: string }> {
const response = await expectOk(await fetchFor(config)(endpoint, { method: "POST", body: form }), destination);
return { response, url: httpUrl(await response.text(), destination) };
}
function remoteName(url: string): string | undefined {
const name = new URL(url).pathname.split("/").filter(Boolean).pop();
return name ? decodeURIComponent(name) : undefined;
}View on GitHub (pinned to 9690622007)
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.
Example fix
// before
{ "options": { "uploadUrl": "file:///srv/uploads" } }
// after
{ "options": { "uploadUrl": "https://example.com/upload" } } Defensive patterns
Strategy: validation
Validate before calling
function assertHttpScheme(v: unknown): void {
const u = typeof v === "string" ? (() => { try { return new URL(v.trim()); } catch { return null; } })() : null;
if (!u || (u.protocol !== "http:" && u.protocol !== "https:")) throw new Error(`URL scheme must be http(s), got: ${u?.protocol ?? v}`);
}
assertHttpScheme(config.options.uploadUrl); Type guard
const isHttpUrl = (v: string): boolean => { try { const u = new URL(v.trim()); return u.protocol === "http:" || u.protocol === "https:"; } catch { return false; } }; Try / catch
try {
uploader = createUploader(config);
} catch (err) {
if (String(err).includes("unsupported upload URL")) throw new Error("only http/https upload URLs are supported");
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Provider delete URL must not embed an account credential
- ${destination} returned an invalid upload URL
- collab.webUrl must not include a query string or fragment
- Absolute paths are not allowed in skill:// URLs
- Path traversal (..) is not allowed in skill:// URLs
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/21d7cec68720d4bc.
Report an issue: GitHub.