thedotmack/claude-mem · error
Gemini API error (status )
Error message
Gemini API error (status ${response.status}) What it means
When the Gemini generateContent endpoint returns a non-2xx status, the provider reads the response body and throws classifyGeminiError with status, bodyText, headers, and requestId attached. The classifier produces a structured error (e.g. rate limit, auth, invalid request) so callers can react per status instead of parsing raw HTTP responses themselves.
Solutions
- Read the classified error's status/body to identify the cause (401/403 → fix API key; 429 → back off; 400 → fix request/model)
- Retry only retryable classifications (429 with retry-after, 5xx) using backoff
- Verify the model name and request payload against the current Gemini API schema
- Check Google's status page for outages when seeing persistent 5xx
Example fix
// before
const text = await provider.query(messages); // throws on 429
// after
try {
const text = await provider.query(messages);
} catch (e) {
if ((e as GeminiError).status === 429) await waitMs(e.retryAfterMs ?? 60000);
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate key and model before calling
if (!process.env.GEMINI_API_KEY) throw new Error('GEMINI_API_KEY missing');
const SUPPORTED_MODELS = ['gemini-2.0-flash', 'gemini-1.5-pro'];
if (!SUPPORTED_MODELS.includes(model)) throw new Error(`unsupported model: ${model}`); Type guard
const isGeminiHttpError = (e: unknown): e is { status: number; bodyText?: string; classification: string; retryAfterMs?: number } =>
typeof e === 'object' && e !== null && typeof (e as any).status === 'number'; Try / catch
try {
const text = await provider.query(messages);
} catch (e) {
if (isGeminiHttpError(e)) {
if (e.status === 429) { await delay(e.retryAfterMs ?? 60000); return retry(); }
if (e.status >= 500) { return backoffRetry(); }
if (e.status === 401 || e.status === 403) { throw new Error('Check GEMINI_API_KEY'); }
}
throw e;
} Prevention
- Branch on the classified status rather than string-matching message text
- Retry only 429 (with retry-after) and 5xx; fail fast on 4xx
- Verify model names against the current Gemini API when a 400 appears
- Monitor GEMINI_API_KEY validity and quota usage
- Check Google's status page during suspected 5xx incidents
When it happens
Trigger: A fetch succeeds at the transport level but the API responds with 4xx/5xx: 429 rate limit/quota, 401/403 bad or missing API key, 400 malformed request or unsupported model, 5xx server-side failure.
Common situations: Expired or revoked GEMINI_API key; model name typo or deprecated model; quota exhausted after heavy multi-turn usage; Gemini incident causing 500/503 responses.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/6b38525442c1bd59.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/worker/GeminiProvider.ts:365
response = await this.fetchGenerateContent(url, contents, priorRequestId, attemptSignal);
} catch (networkError: unknown) {
// Network failures, aborts, DNS, etc.
const err = networkError instanceof Error ? networkError : new Error(String(networkError));
throw classifyGeminiError({
cause: err,
});
}
const requestId = response.headers.get('x-goog-request-id') ?? response.headers.get('x-request-id');
if (requestId) {
priorRequestId = requestId;
} else {
logger.debug('SDK', 'Gemini response missing request-id header; retry dedup is best-effort');
}
if (!response.ok) {
const errorBody = await response.text();
throw classifyGeminiError({
status: response.status,
bodyText: errorBody,
headers: response.headers,
cause: new Error(`Gemini API error (status ${response.status})`),
...(requestId ? { requestId } : {}),
});
}
return await response.json() as GeminiResponse;
}, { label: `Gemini ${model}`, abortSignal: signal, ...(signal ? { maxRetries: 0 } : {}) });
if (!data.candidates?.[0]?.content?.parts?.[0]?.text) {
logger.error('SDK', 'Empty response from Gemini');
return { content: '' };
}
const content = data.candidates[0].content.parts[0].text;
const tokensUsed = data.usageMetadata?.totalTokenCount;View on GitHub (pinned to d8bc9755e7)