can1357/oh-my-pi · error · Error
${context} returned an invalid JSON object
Error message
${context} returned an invalid JSON object What it means
asRecord narrows an unknown parsed-JSON value to a plain object and throws `${context} returned an invalid JSON object` when the value is not a non-array object. It is used to validate API responses (B2 authorization, bucket listings, upload results) before field extraction, so malformed responses fail with a descriptive message naming which API stage misbehaved.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-object-storage.ts:431
resourcePath,
uploadRequest.bytes.byteLength,
uploadRequest.mimeType,
cacheControl,
);
await expectOk(await request(url, { method: "PUT", headers, body: uploadRequest.bytes }), destination);
const deleteHeaders = await azureHeaders("DELETE", accountName, accountKey, resourcePath, 0);
const publicUrl = publicBaseUrl ? publicObjectUrl(publicBaseUrl, key) : url.toString();
return publication(destination, uploadRequest, publicUrl, {
remoteId: key,
delete: { method: "DELETE", url: url.toString(), headers: deleteHeaders },
});
},
};
}
function asRecord(value: unknown, context: string): Record<string, unknown> {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
throw new Error(`${context} returned an invalid JSON object`);
}
return value as Record<string, unknown>;
}
function requiredStringField(value: unknown, field: string, context: string): string {
const result = asRecord(value, context)[field];
if (typeof result !== "string" || result.length === 0) throw new Error(`${context} omitted ${field}`);
return result;
}
function optionalStringField(value: unknown, field: string): string | undefined {
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
const result = (value as Record<string, unknown>)[field];
return typeof result === "string" ? result : undefined;
}
async function sha1Hex(bytes: Uint8Array): Promise<string> {
const digest = new Uint8Array(await crypto.subtle.digest("SHA-1", strictBytes(bytes)));View on GitHub (pinned to 9690622007)
Solutions
- Log the raw response body for the failing call named in `context` to see what was actually returned.
- Verify the API endpoint URL is correct and not intercepted by a proxy/ captive portal.
- Confirm the API version in the URL (e.g. /b2api/v2/) matches the payload the service returns.
- If the response is an HTML error page, resolve the underlying HTTP error (auth, rate limit) first.
Example fix
// before
const value = await response.json(); // may be a string or array
// after
const value = await response.json();
if (typeof value !== "object" || value === null || Array.isArray(value)) {
throw new Error(`unexpected body: ${JSON.stringify(value).slice(0, 200)}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
const raw = await response.text();
let parsed: unknown;
try { parsed = JSON.parse(raw); } catch { throw new Error(`non-JSON body from ${url}: ${raw.slice(0, 120)}`); }
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
throw new Error(`expected JSON object from ${url}, got ${Array.isArray(parsed) ? "array" : typeof parsed}`);
} Type guard
function isJsonObject(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Try / catch
try {
const auth = await authorizeB2(config);
} catch (err) {
if (err instanceof Error && err.message.includes("returned an invalid JSON object")) {
logger.error("object-storage API returned non-object JSON; check proxy/endpoint", { err });
} else throw err;
} Prevention
- log raw response bodies for object-storage calls in debug mode
- bypass or configure proxies for B2/S3 endpoints
- verify API version paths (/b2api/v2/) against current docs
- add an integration check that authorizes against the real endpoint before first upload
When it happens
Trigger: An object-storage API call (b2_authorize_account, b2_list_buckets, upload result parsing) returns JSON that parses to a non-object — a string, number, boolean, array, or null — e.g. a proxy returning a plain-text body, or an endpoint returning a top-level JSON array.
Common situations: Corporate proxy or captive portal returns HTML/text that a lenient JSON parse mangled; B2 API version mismatch returning an error array; hitting the wrong URL (e.g. an auth endpoint that returns a bare token string).
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${context} omitted ${field}
- Backblaze B2 bucket listing omitted buckets
- Backblaze B2 bucket not found: ${bucketName}
- Gemini Files API ${context} response is not a JSON object
- 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/f05415a6894fa2bc.
Report an issue: GitHub.