can1357/oh-my-pi · error · LegacyDestinationError

the upload response did not include a direct image URL

Error message

the upload response did not include a direct image URL

What it means

This LegacyDestinationError is thrown by directJsonUrl() when the parsed JSON response object (including a nested `response` object) contains none of the recognized direct-URL keys: `direct_url`, `directUrl`, `url`, or `URL`. The upload 'succeeded' but the library cannot locate the link to the stored file.

Source

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

	response: Response,
): Promise<Readonly<Record<string, unknown>>> {
	let value: unknown;
	try {
		value = await response.json();
	} catch (error) {
		throw new LegacyDestinationError(destination, "the upload endpoint returned invalid JSON", error);
	}
	const record = objectValue(value);
	for (const _ in record) return record;
	throw new LegacyDestinationError(destination, "the upload endpoint returned an invalid JSON object");
}

function directJsonUrl(destination: BlobDestinationId, record: Readonly<Record<string, unknown>>, base: URL): string {
	const nested = objectValue(record.response);
	const raw =
		firstString(record, ["direct_url", "directUrl", "url", "URL"]) ??
		firstString(nested, ["direct_url", "directUrl", "url", "URL"]);
	if (!raw) throw new LegacyDestinationError(destination, "the upload response did not include a direct image URL");
	return httpUrl(destination, raw, base);
}

function basicAuthorization(username: string, password: string): string {
	return `Basic ${Buffer.from(`${username}:${password}`, "utf8").toString("base64")}`;
}

function optionalBasicHeaders(
	destination: BlobDestinationId,
	config: DestinationRuntimeConfig,
	usernameKey: string,
	passwordKey: string,
): Headers | undefined {
	const username = credentialString(config, usernameKey);
	const password = credentialString(config, passwordKey);
	if (!username && !password) return undefined;
	if (!username || !password) {
		throw new LegacyDestinationError(

View on GitHub (pinned to 9690622007)

Solutions

  1. Configure or patch the endpoint to include a recognized key (`url` or `direct_url`) in its JSON response
  2. Add a server-side response shim that maps the service's actual key (e.g. `download_url`) to `url`
  3. If the API returns only an ID, proxy it and construct the absolute URL server-side
  4. Compare the actual response keys with the accepted aliases: direct_url, directUrl, url, URL (top-level or under `response`)

Example fix

// before (server response)
{"status":"ok","download_url":"https://cdn.example.com/f.png"}
// after
{"status":"ok","url":"https://cdn.example.com/f.png"}
Defensive patterns

Strategy: validation

Validate before calling

const record = await response.json();
const nested = record?.response ?? {};
const hasUrl = ["direct_url","directUrl","url","URL"]
  .some(k => typeof record?.[k] === "string" || typeof nested?.[k] === "string");
if (!hasUrl) throw new Error("endpoint response lacks a direct URL field");

Type guard

function hasDirectUrl(record: unknown): record is Record<string, unknown> & { url: string } {
  const keys = ["direct_url", "directUrl", "url", "URL"];
  const r = record as Record<string, unknown>;
  const nested = (r?.response ?? {}) as Record<string, unknown>;
  return keys.some(k => typeof r?.[k] === "string" || typeof nested?.[k] === "string");
}

Try / catch

try {
  const result = await uploader.upload(request);
} catch (err) {
  if (err instanceof Error && err.message.includes("did not include a direct image URL")) {
    // map the endpoint's actual field name to `url`/`direct_url`
  } else throw err;
}

Prevention

When it happens

Trigger: The endpoint replies with valid JSON lacking any URL field — e.g. `{"status":"ok","id":"abc"}`, or uses a key name outside the four recognized aliases; thrown before httpUrl() validation, called from url() and upload().

Common situations: Custom replacement endpoints with their own response schema (e.g. `download_url` or `link` keys); APIs that return only an identifier requiring a second request to fetch the URL; version drift where the service renamed its response fields.

Related errors


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