can1357/oh-my-pi · error · LegacyDestinationError

the replacement endpoint returned no direct URL

Error message

the replacement endpoint returned no direct URL

What it means

Thrown when the no-credential replacement uploader receives an empty response body after a successful HTTP status. The endpoint is expected to return the direct URL as plain text; an empty body means nothing was uploaded or the endpoint failed silently, so no publication can be created.

Solutions

  1. Verify the endpoint URL points at the actual upload API route, not a root/health path
  2. Test the endpoint manually (curl -F file=@img.png) to see what it returns
  3. Check whether the service is deprecated/shutting down and choose another replacement endpoint
  4. Inspect for a proxy layer stripping response bodies

Example fix

// before: endpoint root, returns empty 200
endpoint: "https://transfer-sh.example/"
// after: exact upload route that echoes the URL
endpoint: "https://transfer-sh.example/upload"
Defensive patterns

Strategy: validation

Validate before calling

// smoke-test the endpoint before relying on it
const probe = await fetch(endpoint, { method: "POST", body: tinyMultipart });
const text = (await probe.text()).trim();
if (!text) throw new Error(`endpoint ${endpoint} returned an empty body`);

Try / catch

try {
	await uploader.upload(request);
} catch (err) {
	if (err instanceof Error && /returned no direct URL/.test(err.message)) {
		// endpoint returned 2xx with empty body — switch endpoints or probe manually
	}
	throw err;
}

Prevention

When it happens

Trigger: POST multipart 'file' (no api key) to the configured endpoint; expectOk passes but response.text().trim() is '' — the server returned 2xx with an empty body.

Common situations: Endpoint misconfigured to a health-check or root URL that 200s with no body; service soft-failing with an empty 200; a reverse proxy swallowing the response; the service shutting down but still answering 200.

Related errors


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

Appendix: source

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

				throw failure(destination, error);
			}
		},
	};
}

function createTransferUploader(config: DestinationRuntimeConfig, endpoint: URL): BlobUploader {
	const destination = "transfer-sh" as const;
	return {
		destination,
		async upload(request) {
			try {
				const response = await fetchFor(config)(endpoint, {
					method: "POST",
					body: multipartFile(request),
				});
				await expectOk(response, destination);
				const raw = (await response.text()).trim();
				if (!raw) throw new LegacyDestinationError(destination, "the replacement endpoint returned no direct URL");
				const deleteUrl = response.headers.get("x-url-delete")?.trim();
				return publication(
					destination,
					request,
					httpUrl(destination, raw, endpoint),
					deleteUrl ? { delete: { method: "DELETE", url: httpUrl(destination, deleteUrl, endpoint) } } : undefined,
				);
			} catch (error) {
				throw failure(destination, error);
			}
		},
	};
}

async function discoverSendSpaceNode(config: DestinationRuntimeConfig, endpoint: URL): Promise<SendSpaceNode> {
	const destination = "sendspace" as const;
	const discovery = new URL(endpoint);
	discovery.searchParams.set("method", "anonymous.uploadGetInfo");

View on GitHub (pinned to 9690622007)