{"record":{"id":"2f4be348693a3239","repo":"thedotmack/claude-mem","slug":"parse-error-2f4be3","errorCode":"parse_error","errorMessage":"Gemini returned invalid JSON","messagePattern":"Gemini returned invalid JSON","errorType":"exception","errorClass":"ServerClassifiedProviderError","httpStatus":null,"severity":"error","filePath":"src/server/generation/providers/GeminiObservationProvider.ts","lineNumber":188,"sourceCode":"      });\n    }\n\n    if (!response.ok) {\n      const bodyText = await safeReadBody(response);\n      throw classifyGeminiServerError({\n        status: response.status,\n        bodyText,\n        headers: response.headers,\n        cause: new Error(`Gemini HTTP error (status ${response.status})`),\n      });\n    }\n\n    let data: GeminiResponse;\n    try {\n      data = (await response.json()) as GeminiResponse;\n    } catch (parseError) {\n      const err = parseError instanceof Error ? parseError : new Error(String(parseError));\n      throw new ServerClassifiedProviderError('Gemini returned invalid JSON', {\n        kind: 'parse_error',\n        cause: err,\n      });\n    }\n\n    if (data.error) {\n      throw classifyGeminiServerError({\n        status: response.status,\n        bodyText: `${data.error.status ?? ''} ${data.error.message ?? ''}`,\n        headers: response.headers,\n        cause: new Error(`Gemini HTTP error (status ${response.status})`),\n      });\n    }\n\n    const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? '';\n    if (!rawText) {\n      logger.warn('SDK', 'Gemini returned empty content', { provider: 'gemini', model: this.model });\n    }","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/server/generation/providers/GeminiObservationProvider.ts#L170-L206","documentation":"The Gemini provider could not parse the HTTP response body from the Google Generative Language API as JSON while running `generate`. The underlying SyntaxError from `response.json()` is wrapped as a ServerClassifiedProviderError with kind 'parse_error'. GeminiResponses that fail include HTML error pages, empty bodies, and malformed payloads.","triggerScenarios":"Calling GeminiObservationProvider.generate() when the Gemini endpoint returns a non-JSON body: HTML from proxies or a captive portal, empty 5xx bodies, gzip/streaming truncation, or the URL resolving to something other than the API (bad base URL, blocked region interstitial).","commonSituations":"VPN/corporate network injecting HTML blocks; Google outage or quota page returned as HTML; misconfigured proxy stripping response bodies; wrong model path returning a non-JSON error page; intermittent truncation on large responses.","solutions":["Capture response.status and the first bytes of the raw body to identify what was returned instead of JSON","Confirm the request reaches generativelanguage.googleapis.com without proxy interference (bypass proxy for this host if needed)","Check the model name and API version in the request path — some invalid paths return HTML/empty bodies","Catch the classified parse_error and implement retry-with-backoff for transient gateway failures"],"exampleFix":"// before\ndata = (await response.json()) as GeminiResponse;\n\n// after: surface the real payload on parse failure\nconst raw = await response.text();\ntry {\n  data = JSON.parse(raw) as GeminiResponse;\n} catch (e) {\n  throw new ServerClassifiedProviderError(`Gemini returned non-JSON (status ${response.status}): ${raw.slice(0, 200)}`, { kind: 'parse_error', cause: e as Error });\n}","handlingStrategy":"try-catch","validationCode":"const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;\nnew URL(url); // throws early if the composed URL is malformed","typeGuard":"function isGeminiResponse(v: unknown): v is GeminiResponse {\n  return typeof v === 'object' && v !== null &&\n    ((v as any).error !== undefined || (v as any).candidates !== undefined || (v as any).promptFeedback !== undefined);\n}","tryCatchPattern":"try {\n  const result = await provider.generate(input);\n} catch (e) {\n  if (e instanceof ServerClassifiedProviderError && e.kind === 'parse_error') {\n    logger.warn('Gemini returned non-JSON body', { cause: e.cause });\n    await sleep(backoff());\n    return retryWithBackoff(() => provider.generate(input), 3);\n  }\n  throw e;\n}","preventionTips":["Check response.ok and read raw text before JSON.parse to capture real payloads","Bypass corporate proxies for Google API hosts or add retry for gateway HTML responses","Use exponential backoff for transient gateway errors","Pin model names and API versions to avoid undocumented error pages"],"tags":["json-parse","gemini","http-response","provider"],"backgroundTag":"invalid-json-response","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}