can1357/oh-my-pi · error

Gemini Files API ${context} response is not valid JSON

Error message

Gemini Files API ${context} response is not valid JSON

What it means

This is thrown by responseJson() when the Gemini Files API HTTP response body cannot be parsed as JSON, or parses to a non-object (null/array). The library only surfaces Google's response payload after an HTTP 2xx, so this means the 2xx body itself was malformed (HTML error page, empty body, truncated stream, or a proxy MITM). It wraps the underlying SyntaxError with a stable message that includes the request context (e.g. 'finalize').

Source

Thrown at packages/coding-agent/src/blob-broker/provider-files-gemini.ts:28

	name: string;
	uri: string;
	mimeType: string;
	expiresAt: number;
}

function responseObject(value: unknown, context: string): Record<string, unknown> {
	if (typeof value !== "object" || value === null || Array.isArray(value)) {
		throw new Error(`Gemini Files API ${context} response is not a JSON object`);
	}
	return value as Record<string, unknown>;
}

async function responseJson(response: Response, context: string): Promise<Record<string, unknown>> {
	try {
		return responseObject((await response.json()) as unknown, context);
	} catch (error) {
		if (error instanceof Error && error.message.startsWith("Gemini Files API")) throw error;
		throw new Error(`Gemini Files API ${context} response is not valid JSON`);
	}
}

function requireString(value: unknown, field: string): string {
	if (typeof value !== "string" || value.length === 0) {
		throw new Error(`Gemini Files API finalize response is missing ${field}`);
	}
	return value;
}

function parseFinalizedFile(payload: Record<string, unknown>): GeminiFileResource {
	const file = responseObject(payload.file, "finalize file");
	const state = requireString(file.state, "file.state");
	if (state !== "ACTIVE") throw new Error("Gemini Files API finalized file state is not ACTIVE");

	const name = requireString(file.name, "file.name");
	if (!/^files\/[^/]+$/.test(name)) {
		throw new Error("Gemini Files API finalize response contains an invalid file.name");

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the raw response (curl the upload/finalize endpoint with your API key) to see what non-JSON body is actually returned.
  2. Rule out proxies/VPNs/captive portals intercepting generativelanguage.googleapis.com traffic.
  3. Retry the upload — transient truncation of the 2xx body is the most common cause.
  4. If using a custom FetchImpl (tests, SDK embed), verify it returns a proper JSON Response with correct Content-Type.

Example fix

// before: assuming every 2xx body is JSON
const data = await finalizeResponse.json();
// after: tolerate transient bad bodies with a retry
let data;
for (let attempt = 0; attempt < 3; attempt++) {
  try { data = await client.upload(request); break; }
  catch (e) {
    if (attempt === 2 || !String(e.message).includes("not valid JSON")) throw e;
    await Bun.sleep(1000);
  }
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

function looksLikeGeminiJson(text: string): boolean {
  try { const v = JSON.parse(text); return v !== null && typeof v === "object" && !Array.isArray(v); }
  catch { return false; }
}

Try / catch

try {
  const handle = await client.upload(request);
} catch (error) {
  if (error instanceof Error && error.message.includes("is not valid JSON")) {
    // transient bad body: retry, or inspect raw traffic via a wrapper fetchImpl
  } else throw error;
}

Prevention

When it happens

Trigger: parseFinalizedFile's caller invokes responseJson(finalizeResponse, "finalize") after a successful resumable-upload finalize POST; response.json() throws a SyntaxError, or responseObject throws a non-Gemini-prefixed error, causing the rethrow at provider-files-gemini.ts:28.

Common situations: Corporate proxy or captive portal returning HTML instead of JSON; API returning 200 with an empty body during partial outages; response body truncated mid-stream; a stubbed fetchImpl returning a non-JSON Response in tests.

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.

Related errors


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