can1357/oh-my-pi · error · LegacyDestinationError

the upload node did not return a direct image URL

Error message

the upload node did not return a direct image URL

What it means

Thrown after a POST to the legacy upload node when the response status is ok but the XML body lacks status=ok or a direct_url/download_url element, meaning the node accepted the request but did not report a usable image URL. The library cannot publish without a direct link, so it raises LegacyDestinationError.

Source

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

function createSendSpaceUploader(config: DestinationRuntimeConfig, endpoint: URL): BlobUploader {
	const destination = "sendspace" as const;
	return {
		destination,
		async upload(request: BlobUploadRequest) {
			try {
				const node = await discoverSendSpaceNode(config, endpoint);
				const body = multipartFile(request, "userfile", {
					MAX_FILE_SIZE: node.maxFileSize,
					UPLOAD_IDENTIFIER: node.uploadIdentifier,
					extra_info: node.extraInfo,
				});
				const response = await fetchFor(config)(node.url, { method: "POST", body });
				await expectOk(response, destination);
				const text = await response.text();
				const status = xmlElement(text, "status");
				const raw = xmlElement(text, "direct_url") ?? xmlElement(text, "download_url");
				if (status !== "ok" || !raw) {
					throw new LegacyDestinationError(destination, "the upload node did not return a direct image URL");
				}
				const deleteUrl = xmlElement(text, "delete_url");
				return publication(
					destination,
					request,
					httpUrl(destination, raw),
					deleteUrl ? { delete: { method: "GET", url: httpUrl(destination, deleteUrl) } } : undefined,
				);
			} catch (error) {
				throw failure(destination, error);
			}
		},
	};
}

function incompatible(destination: BlobDestinationId, reason: string): never {
	throw new DestinationUnavailableError(destination, reason);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the raw response body (log `text` before the throw) to see the actual status/error element the node returned.
  2. Verify the API key/token field name and value expected by the host's multipart form.
  3. Confirm the host still uses direct_url/download_url element names; update the destination config or host version accordingly.
  4. Retry with a smaller file to rule out size-limit rejections.

Example fix

// before
const raw = xmlElement(text, "direct_url") ?? xmlElement(text, "download_url");
// host renamed the element:
// after
const raw = xmlElement(text, "direct_url") ?? xmlElement(text, "download_url") ?? xmlElement(text, "url"); // match the host's current schema
Defensive patterns

Strategy: try-catch

Type guard

function hasDirectUrl(xml: string): boolean {
  return /<status>ok<\/status>/.test(xml) && /<(direct_url|download_url)>[^<]+<\/(direct_url|download_url)>/.test(xml);
}

Try / catch

try {
  const result = await publish(destination, request);
} catch (err) {
  if (err instanceof LegacyDestinationError && err.message.includes("direct image URL")) {
    logger.warn("legacy upload node gave no direct_url; inspecting response", { destination });
    // retry with a smaller file or different destination
  } else throw err;
}

Prevention

When it happens

Trigger: POSTing the multipart body to the discovery-provided node URL returns XML without <status>ok</status> or without a <direct_url>/<download_url> element — e.g. the node rejected the file silently, returned a rate-limit or auth-error XML, or uses different element names.

Common situations: Image host changed its response schema (renamed direct_url); upload rejected due to missing API key/token in the multipart form; file exceeds the node's size limit but the host signals it via a non-ok status; interim HTML error page replaces the XML response.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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