thedotmack/claude-mem · error · ServerClassifiedProviderError
parse_error
parse_error
Error message
Gemini returned invalid JSON
What it means
Thrown by GeminiObservationProvider.generate() when response.json() rejects after a successful HTTP status. Classified as parse_error, it means the Gemini endpoint returned a non-JSON body (HTML, empty, or truncated). The underlying parse error is attached as cause.
Source
Thrown at src/server/generation/providers/GeminiObservationProvider.ts:188
});
}
if (!response.ok) {
const bodyText = await safeReadBody(response);
throw classifyGeminiServerError({
status: response.status,
bodyText,
headers: response.headers,
cause: new Error(`Gemini HTTP error (status ${response.status})`),
});
}
let data: GeminiResponse;
try {
data = (await response.json()) as GeminiResponse;
} catch (parseError) {
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
throw new ServerClassifiedProviderError('Gemini returned invalid JSON', {
kind: 'parse_error',
cause: err,
});
}
if (data.error) {
throw classifyGeminiServerError({
status: response.status,
bodyText: `${data.error.status ?? ''} ${data.error.message ?? ''}`,
headers: response.headers,
cause: new Error(`Gemini HTTP error (status ${response.status})`),
});
}
const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? '';
if (!rawText) {
logger.warn('SDK', 'Gemini returned empty content', { provider: 'gemini', model: this.model });
}View on GitHub (pinned to d768ba3643)
Solutions
- Inspect the cause and the status/bodyText captured just before parsing — a 2xx with non-JSON points at interception.
- Retry once; transient interception often clears.
- Bypass or allowlist the proxy for the Gemini host.
- Verify the base URL resolves to the real Gemini API.
- Capture the raw body to confirm HTML/empty, then escalate.
Example fix
// before: parse without inspecting body shape
// after: guard non-JSON before parsing
const bodyText = await response.text();
if (!bodyText.trim().startsWith('{')) {
throw new Error('Gemini returned non-JSON body');
}
const data = JSON.parse(bodyText) as GeminiResponse; Defensive patterns
Strategy: retry
Validate before calling
async function fetchGeminiJsonOrThrow(response: Response): Promise<unknown> {
const bodyText = await response.text();
if (!response.ok) {
throw new Error(`Gemini HTTP error (status ${response.status})`);
}
if (!bodyText || !bodyText.trim().startsWith('{')) {
throw new Error('Gemini returned a non-JSON body (possible proxy interception)');
}
return JSON.parse(bodyText);
} Type guard
function isGeminiResponse(v: unknown): v is GeminiResponse {
return typeof v === 'object' && v !== null
&& 'candidates' in v;
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await provider.generate(context);
} catch (error) {
const isParse = error instanceof ServerClassifiedProviderError && error.kind === 'parse_error';
if (!isParse || attempt === 2) throw error;
await new Promise(r => setTimeout(r, 2 ** attempt * 500));
}
} Prevention
- Inspect the raw body prefix before parsing to detect HTML/empty responses.
- Bypass or allowlist proxies for the Gemini host.
- Confirm the base URL targets the real Gemini API.
- Retry parse errors with backoff since interception is often transient.
When it happens
Trigger: A proxy/WAF returns HTML in place of the JSON response; the Gemini endpoint returns 200 with an empty body; a gateway truncates the stream; the configured base URL points at the wrong host; a transient Google status page is served.
Common situations: Corporate proxy interception. Google serves an interim HTML page during an outage. A wrong base URL hits a CDN returning HTML. Network instability truncates the response.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- parse_error
- parse_error
- auth_invalid
- SSE stream returned HTTP ${response.status}
- ${timeoutMessage} (timed out after ${timeoutMs}ms)
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/2f4be348693a3239.
Report an issue: GitHub.