can1357/oh-my-pi · error · LegacyDestinationError

the discovery endpoint rejected the upload request

Error message

the discovery endpoint rejected the upload request

What it means

Thrown during the SendSpace-style discovery phase: the uploader GETs the endpoint's API (optionally with api_key) expecting an XML document describing the upload URL. If the XML contains status="fail" (quoted, case-insensitive), the discovery request itself was rejected — usually an authentication problem — before any file upload is attempted.

Source

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

			}
		},
	};
}

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");
	discovery.searchParams.set("speed_limit", "0");
	discovery.searchParams.set("api_version", "1.0");
	discovery.searchParams.set("app_version", "1.0");
	const apiKey = credentialString(config, "apiKey");
	if (apiKey) discovery.searchParams.set("api_key", apiKey);
	const response = await fetchFor(config)(discovery, { method: "GET" });
	await expectOk(response, destination);
	const xml = await response.text();
	if (/\bstatus=(?:"fail"|'fail')/i.test(xml)) {
		throw new LegacyDestinationError(destination, "the discovery endpoint rejected the upload request");
	}
	const rawUrl = xmlAttribute(xml, "upload", "url");
	const maxFileSize = xmlAttribute(xml, "upload", "max_file_size");
	const uploadIdentifier = xmlAttribute(xml, "upload", "upload_identifier");
	const extraInfo = xmlAttribute(xml, "upload", "extra_info");
	if (!rawUrl || !maxFileSize || !uploadIdentifier || !extraInfo) {
		throw new LegacyDestinationError(destination, "the discovery endpoint returned incomplete upload-node metadata");
	}
	return {
		url: new URL(httpUrl(destination, rawUrl)),
		maxFileSize,
		uploadIdentifier,
		extraInfo,
	};
}

function createSendSpaceUploader(config: DestinationRuntimeConfig, endpoint: URL): BlobUploader {
	const destination = "sendspace" as const;

View on GitHub (pinned to 9690622007)

Solutions

  1. Set/fix the apiKey credential for the sendspace destination
  2. Inspect the returned XML (wrapped as failure cause) for the detailed error element
  3. Confirm the private endpoint expects api_key as a GET query parameter
  4. Ensure the endpoint host is not the deprecated public api.sendspace.com (blocked separately)
  5. Test the discovery URL manually in a browser/curl to see the failure detail

Example fix

// before: no key configured
config: { endpoint: "https://my-clone.example/api" }
// after
config: { endpoint: "https://my-clone.example/api", credentials: { apiKey: process.env.SENDSPACE_KEY } }
Defensive patterns

Strategy: try-catch

Validate before calling

if (!apiKey) throw new Error("sendspace-compatible discovery requires an apiKey credential");
// also ensure the endpoint is private, not the blocked public API
if (/api\.sendspace\.com$/.test(new URL(endpoint).hostname)) throw new Error("use a private discovery endpoint");

Try / catch

try {
	await uploader.upload(request);
} catch (err) {
	if (err instanceof Error && /discovery endpoint rejected the upload request/.test(err.message) && err.cause) {
		const xml = String(err.cause);
		const detail = xml.match(/<error[^>]*>([^<]+)<\/error>/)?.[1];
		logger.warn("discovery failed", { detail });
	}
	throw err;
}

Prevention

When it happens

Trigger: GET to the configured sendspace-compatible discovery endpoint returns XML with status="fail"/'fail' — invalid api_key, wrong token, or the private API refusing the request.

Common situations: Missing or wrong apiKey in destination credentials; pointing the endpoint at a SendSpace clone whose auth differs; api_key passed in query but the service expects it as POST; account suspended.

Related errors


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