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

  1. Read the classified error's status/body to identify the cause (401/403 → fix API key; 429 → back off; 400 → fix request/model)
  2. Retry only retryable classifications (429 with retry-after, 5xx) using backoff
  3. Verify the model name and request payload against the current Gemini API schema
  4. 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

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)