can1357/oh-my-pi · error · LegacyDestinationError
the upload endpoint returned an invalid JSON object
Error message
the upload endpoint returned an invalid JSON object
What it means
This LegacyDestinationError is thrown by jsonObject() when the response body parses as JSON but is not a non-empty plain object (arrays, null, strings, numbers, and empty objects are all rejected). The uploaders index into the parsed value with string keys, so they require an object with at least one property.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-legacy.ts:133
const value = record[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
return undefined;
}
async function jsonObject(
destination: BlobDestinationId,
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,View on GitHub (pinned to 9690622007)
Solutions
- Adjust the endpoint to return a JSON object containing the upload result fields (url/direct_url)
- If the endpoint returns an array or scalar, wrap it server-side in an object
- Check the response body to confirm it is `{}`/array/null and compare with the schema the uploader expects
Defensive patterns
Strategy: type-guard
Type guard
function isNonEmptyJsonObject(value: unknown): value is Record<string, unknown> {
return (
typeof value === "object" && value !== null && !Array.isArray(value) &&
Object.keys(value).length > 0
);
} Try / catch
try {
const result = await uploader.upload(request);
} catch (err) {
if (err instanceof Error && err.message.includes("invalid JSON object")) {
// body parsed but was null/array/scalar/{} — fix server response shape
} else throw err;
} Prevention
- Ensure upload endpoints return a JSON object with result fields, never {} or arrays
- Compare your endpoint's success body with the uploader's expected schema
- Add an integration test asserting the parsed response is a non-empty object
- Guard against APIs that acknowledge success with empty bodies
When it happens
Trigger: The endpoint returns `null`, `[]`, `"ok"`, `42`, or `{}` — e.g. an upload API that acknowledges success with an empty JSON object or a bare array of URLs; thrown right after the invalid-JSON check in jsonObject(), consumed by the `data` helper.
Common situations: Replacement endpoints whose success response is `{}` or a JSON array; APIs returning JSON-RPC style arrays; servers echoing empty bodies with a JSON content-type.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- the upload endpoint returned invalid JSON
- the upload response did not include a direct image URL
- the upload response did not contain a valid direct URL
- the upload response URL must use HTTP or HTTPS
- Gemini Files API ${context} response is not valid JSON
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8efd373190dcb54b.
Report an issue: GitHub.