can1357/oh-my-pi · error · LegacyDestinationError

the replacement endpoint rejected the upload

Error message

the replacement endpoint rejected the upload

What it means

Thrown by the replacement-endpoint uploader (e.g. chevereto-style) when the server responds OK but its comma-separated 'status,url[,deleteUrl]' payload does not parse: the first field is not a non-negative integer or the URL field is missing. The library cannot construct a publication URL, so the upload is treated as rejected by the replacement endpoint.

Source

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

			}
		},
	};
}

function createPuushUploader(config: DestinationRuntimeConfig, endpoint: URL): BlobUploader {
	const destination = "puush" as const;
	const apiKey = requireCredential(config, "apiKey");
	return {
		destination,
		async upload(request) {
			try {
				const body = multipartFile(request, "f", { k: apiKey, z: "oh-my-pi" });
				const response = await fetchFor(config)(endpoint, { method: "POST", body });
				await expectOk(response, destination);
				const values = (await response.text()).trim().split(",");
				const status = Number.parseInt(values[0] ?? "", 10);
				if (!Number.isInteger(status) || status < 0 || !values[1]) {
					throw new LegacyDestinationError(destination, "the replacement endpoint rejected the upload");
				}
				const url = httpUrl(destination, values[1]);
				return publication(destination, request, url, values[2] ? { remoteId: values[2] } : undefined);
			} catch (error) {
				throw failure(destination, error);
			}
		},
	};
}

function createMediaFireUploader(config: DestinationRuntimeConfig, endpoint: URL): BlobUploader {
	const destination = "mediafire" as const;
	const headers = optionalBasicHeaders(destination, config, "username", "password");
	return {
		destination,
		async upload(request) {
			try {
				const fields: Record<string, string> = {};

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the endpoint URL and apiKey configured for this destination
  2. Inspect the raw response text (it is embedded via failure()) to see what the server returned
  3. Confirm the endpoint speaks the expected 'status,url[,deleteUrl]' CSV protocol
  4. Point the destination at a service matching the expected response format

Example fix

// before: endpoint pointed at a generic host
endpoint: "https://example.com/api/upload"
// after: use the actual chevereto-compatible API endpoint with valid key
endpoint: "https://my-chevereto-host.example/api/upload",
credentials: { apiKey: process.env.CHEVERETO_API_KEY }
Defensive patterns

Strategy: try-catch

Validate before calling

// validate endpoint speaks the CSV protocol with a dry run or docs check
if (!endpointUrl.pathname.includes("api")) console.warn("endpoint may not follow the status,url CSV protocol");

Type guard

function isCsvUploadResult(text: string): boolean {
	const parts = text.trim().split(",");
	const status = Number.parseInt(parts[0] ?? "", 10);
	return Number.isInteger(status) && status >= 0 && Boolean(parts[1]);
}

Try / catch

try {
	await uploader.upload(request);
} catch (err) {
	if (err instanceof Error && /replacement endpoint rejected the upload/.test(err.message) && err.cause) {
		console.error("raw endpoint response:", err.cause);
	}
	throw err;
}

Prevention

When it happens

Trigger: POST multipart 'f' with 'k' (apiKey) and 'z' branding to the user-configured endpoint; expectOk passes but response.text() splits on commas into an unparseable status or empty URL — e.g. body is 'error,Invalid key' or an HTML page.

Common situations: Wrong API key for the replacement host; endpoint returning an error page with 200 status; misconfigured endpoint pointing at a non-chevereto-style service; the service changing its response format.

Related errors


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