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

  1. Change the option to an http:// or https:// URL.
  2. If the destination is local-only, upload it directly rather than through this HTTP uploader.
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/21d7cec68720d4bc. Report an issue: GitHub.