can1357/oh-my-pi · error · LegacyDestinationError

the upload response did not contain a valid direct URL

Error message

the upload response did not contain a valid direct URL

What it means

This LegacyDestinationError is thrown by httpUrl() when a URL string extracted from an upload endpoint's HTTP response cannot be parsed by `new URL(raw)` (optionally resolved against the request's base endpoint URL). It indicates the remote service returned a malformed 'direct URL' field, and the underlying parse failure is attached as `cause`.

Source

Thrown at packages/coding-agent/src/blob-broker/uploaders-legacy.ts:100

		}
	}
	return endpoint;
}

function sendSpaceEndpoint(config: DestinationRuntimeConfig): URL {
	const endpoint = configuredEndpoint("sendspace", config);
	if (endpoint.hostname.toLowerCase() === SENDSPACE_DEFAULT_HOST) {
		throw new DestinationUnavailableError("sendspace", "the deprecated public discovery endpoint cannot be used");
	}
	return endpoint;
}

function httpUrl(destination: BlobDestinationId, raw: string, base?: URL): string {
	let url: URL;
	try {
		url = base ? new URL(raw, base) : new URL(raw);
	} catch (error) {
		throw new LegacyDestinationError(destination, "the upload response did not contain a valid direct URL", error);
	}
	if (url.protocol !== "https:" && url.protocol !== "http:") {
		throw new LegacyDestinationError(destination, "the upload response URL must use HTTP or HTTPS");
	}
	return url.href;
}

function objectValue(value: unknown): Readonly<Record<string, unknown>> {
	if (!value || typeof value !== "object" || Array.isArray(value)) return {};
	return value as Readonly<Record<string, unknown>>;
}

function firstString(record: Readonly<Record<string, unknown>>, keys: readonly string[]): string | undefined {
	for (const key of keys) {
		const value = record[key];
		if (typeof value === "string" && value.trim()) return value.trim();
	}
	return undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the raw HTTP response (log it or use a proxy) to see what the endpoint actually returned in the URL field
  2. Fix or replace the custom endpoint so it returns absolute http(s) URLs
  3. If the endpoint returns relative paths, front it with a proxy that rewrites them to absolute URLs
  4. Check the error's `cause` for the exact URL parse failure to identify the offending string
Defensive patterns

Strategy: try-catch

Type guard

function isAbsoluteHttpUrl(value: unknown): value is string {
  if (typeof value !== "string") return false;
  try {
    const u = new URL(value);
    return u.protocol === "https:" || u.protocol === "http:";
  } catch {
    return false;
  }
}

Try / catch

try {
  const result = await uploader.upload(request);
} catch (err) {
  if (err instanceof Error && err.message.includes("did not contain a valid direct URL")) {
    console.error("cause:", err.cause); // inspect what the server returned
  } else throw err;
}

Prevention

When it happens

Trigger: An upload completes and the response JSON contains a `direct_url`/`directUrl`/`url`/`URL` field whose value is not a parseable URL — e.g. a relative path like '/files/abc.png' when no base resolution works, empty string that slipped past firstString, HTML snippet, or a truncated URL from a misbehaving proxy; called from directJsonUrl, url(), upload(), and discoverSendSpaceNode.

Common situations: Uploading to a self-hosted replacement endpoint that returns relative paths instead of absolute URLs; a reverse proxy injecting an error page whose extracted string is not a URL; service changed its response schema.

Related errors


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