Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

Foutafhandeling in Gemini API-apps

Bouw robuuste Gemini-applicaties met correcte afhandeling van HTTP-foutcodes, safety-blokkades, context-limieten en netwerkstoringen via de google-genai SDK.

Fouttypen in de Gemini API

Gemini-fouten vallen in twee categorieën. HTTP-fouten betekenen dat de request de API bereikte maar werd geweigerd of mislukte. Content-fouten betekenen dat de request werd verwerkt, maar dat het resultaat niet bruikbaar is, bijvoorbeeld omdat een safety-filter het antwoord blokkeerde.

Sinds 2025 is de aanbevolen Python-bibliotheek de google-genai SDK (import from google import genai). De oudere google.generativeai SDK is uitgefaseerd en krijgt geen nieuwe modellen of features meer. De voorbeelden hieronder gebruiken daarom de actuele SDK.

lightbulb

Houd je SDK actueel

Update regelmatig met pip install -U google-genai. Oudere SDK-versies sturen request-formaten die de API niet meer accepteert, wat onnodige 400-fouten oplevert.

HTTP-foutcodes

Code Naam Oorzaak Oplossing Retry
400 INVALID_ARGUMENT Ongeldig request-formaat Controleer de request-structuur Nee
403 PERMISSION_DENIED Ongeldige sleutel, ontbrekende permissies of regio zonder billing Controleer je GEMINI_API_KEY, IAM-permissies of facturatie Nee
404 NOT_FOUND Onbekend model of resource Controleer de modelnaam en API-versie Nee
429 RESOURCE_EXHAUSTED Rate limit of quota bereikt Exponentiële backoff toepassen Ja
500 INTERNAL Google-zijdige fout, soms te lange context Context verkleinen, wachten en opnieuw proberen Ja
503 UNAVAILABLE Model tijdelijk overbelast of offline Wachten en opnieuw proberen Ja
504 DEADLINE_EXCEEDED Prompt of context te groot voor de tijdslimiet Verhoog je client-timeout of verklein de context Ja

Let op: authenticatieproblemen geven bij de Gemini API een 403 PERMISSION_DENIED, niet 401. Een 401 is geen standaard Gemini-respons.

Foutafhandeling met de google-genai SDK

De SDK gooit errors.APIError voor fouten van de modelservice. Daaronder vallen errors.ClientError (4xx) en errors.ServerError (5xx). Het exacte HTTP-statusnummer staat in het code-attribuut, zodat je per code een strategie kunt kiezen.

from google import genai
from google.genai import errors
import time
import random
import logging
from enum import Enum

logger = logging.getLogger(__name__)

client = genai.Client(api_key="GEMINI_API_KEY")

class RetryStrategy(Enum):
    NO_RETRY = "no_retry"
    EXPONENTIAL = "exponential"

RETRYABLE_CODES = {429, 500, 503, 504}

def strategy_for_code(code: int) -> RetryStrategy:
    if code in RETRYABLE_CODES:
        return RetryStrategy.EXPONENTIAL
    return RetryStrategy.NO_RETRY

def generate_with_full_error_handling(
    model: str,
    prompt: str,
    max_retries: int = 5,
) -> tuple:
    for attempt in range(max_retries + 1):
        try:
            response = client.models.generate_content(
                model=model,
                contents=prompt,
            )

            feedback = response.prompt_feedback
            if feedback and feedback.block_reason:
                return None, f"Prompt geblokkeerd: {feedback.block_reason.name}"

            candidates = response.candidates
            if not candidates:
                return None, "Geen kandidaatantwoorden gegenereerd"

            candidate = candidates[0]
            if candidate.finish_reason and candidate.finish_reason.name == "SAFETY":
                categorien = [
                    r.category.name for r in (candidate.safety_ratings or [])
                    if r.probability and r.probability.name in ("HIGH", "MEDIUM")
                ]
                return None, f"Antwoord geblokkeerd door safety: {categorien}"

            if candidate.finish_reason and candidate.finish_reason.name == "MAX_TOKENS":
                logger.warning("Antwoord afgekort door MAX_TOKENS")

            return response.text, None

        except errors.APIError as e:
            strategy = strategy_for_code(e.code)

            if strategy == RetryStrategy.NO_RETRY or attempt == max_retries:
                logger.error(f"Fatale fout {e.code}: {e.message}")
                return None, e.message

            wait_time = (2 ** attempt) + random.uniform(0, 1)
            logger.warning(
                f"Fout {e.code}, poging {attempt + 1}/{max_retries}. Wacht {wait_time:.1f}s"
            )
            time.sleep(wait_time)

    return None, "Maximaal aantal pogingen bereikt"

De random.uniform(0, 1) voegt jitter toe. Zonder die ruis proberen meerdere clients precies tegelijk opnieuw, wat de overbelasting in stand houdt.

Safety-fouten specifiek afhandelen

Een blokkade kan op twee plekken ontstaan: in de prompt (prompt_feedback.block_reason) of in het gegenereerde antwoord (finish_reason == SAFETY). Behandel ze apart, want de boodschap aan de gebruiker verschilt.

def handle_safety_block(response) -> dict:
    result = {
        "blocked": False,
        "reason": None,
        "categories": [],
        "text": None,
    }

    feedback = response.prompt_feedback
    if feedback and feedback.block_reason:
        result["blocked"] = True
        result["reason"] = "prompt_blocked"
        result["categories"] = [feedback.block_reason.name]
        return result

    if response.candidates:
        candidate = response.candidates[0]
        if candidate.finish_reason and candidate.finish_reason.name == "SAFETY":
            result["blocked"] = True
            result["reason"] = "output_blocked"
            result["categories"] = [
                r.category.name
                for r in (candidate.safety_ratings or [])
                if r.probability and r.probability.name in ("HIGH", "MEDIUM")
            ]
            return result

        if candidate.content and candidate.content.parts:
            result["text"] = candidate.content.parts[0].text

    return result
warning

Safety is geen crash

Behandel safety-blokkades nooit als onherstelbare fouten in je UX. Geef gebruikers een begrijpelijke melding en, waar relevant, een suggestie om hun vraag te herformuleren.

Context-limiet fouten

Bij erg lange invoer loop je tegen het context-venster aan of krijg je een 500 of 504 omdat de verwerking te zwaar wordt. Splits de tekst dan in stukken en verwerk die afzonderlijk.

def split_and_process(model: str, long_text: str, chunk_size: int = 50000) -> list:
    words = long_text.split()
    chunks = []
    current = []

    for word in words:
        current.append(word)
        if len(current) >= chunk_size:
            chunks.append(" ".join(current))
            current = []

    if current:
        chunks.append(" ".join(current))

    results = []
    for i, chunk in enumerate(chunks):
        prompt = f"Verwerk deel {i + 1}/{len(chunks)}:
{chunk}"
        text, error = generate_with_full_error_handling(model, prompt)
        if error:
            logger.error(f"Deel {i + 1} mislukt: {error}")
        results.append({"deel": i + 1, "tekst": text, "fout": error})

    return results

Foutlogging voor productie

Log fouten gestructureerd, zodat je ze later kunt filteren op error_code en patronen kunt herkennen (bijvoorbeeld een piek aan 429's die op te lage quota wijst).

import json
import traceback
from datetime import datetime, timezone

def log_api_error(error: Exception, context: dict) -> None:
    log_entry = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "error_type": type(error).__name__,
        "error_code": getattr(error, "code", None),
        "error_message": str(error),
        "context": context,
        "stacktrace": traceback.format_exc(),
    }
    logger.error("gemini_api_error", extra={"data": json.dumps(log_entry)})

Rate limits in de praktijk

De free tier is in december 2025 verlaagd: het aantal verzoeken per minuut ligt nu lager dan voorheen, waardoor apps die eerder werkten ineens 429-fouten konden krijgen. Reken niet op de free-tier limieten voor productie. Limieten gelden over drie assen: verzoeken per minuut (RPM), tokens per minuut (TPM) en verzoeken per dag (RPD), die rond middernacht Pacific Time reset. Schakel facturatie in of vraag een quotaverhoging aan voordat je live gaat, en respecteer altijd de exponentiële backoff uit het voorbeeld hierboven.

Hoe onderscheid ik een tijdelijke fout van een permanente?

Kijk naar de code op de APIError. De codes 429, 500, 503 en 504 zijn tijdelijk en lenen zich voor retry. De overige 4xx-fouten (400, 403, 404) zijn permanent: je request klopt niet en opnieuw proberen helpt niet. Codeer dit expliciet, zoals in het voorbeeld met RETRYABLE_CODES.

Welke SDK moet ik gebruiken, google.generativeai of google-genai?

Gebruik google-genai (import from google import genai). Dat is de actuele, ondersteunde bibliotheek voor zowel de Gemini Developer API als Vertex AI. De oudere google.generativeai is uitgefaseerd en ondersteunt nieuwe modellen niet meer.

Wat doe ik als mijn API-sleutel niet werkt?

Een ongeldige of niet-geautoriseerde sleutel geeft een 403 PERMISSION_DENIED, niet 401. Controleer of de sleutel volledig en zonder spaties is overgenomen en of hij rechten heeft op het gekozen model. Bij Vertex AI controleer je de service-account credentials en hun vervaldatum.

Hoe vang ik specifiek een rate limit (429) op?

Vang errors.APIError (of errors.ClientError) en lees e.code. Is die 429, pas dan exponentiële backoff met jitter toe en probeer opnieuw. Houd ook rekening met de RPM-, TPM- en RPD-limieten van je tier, want een 429 kan op elk van die drie slaan.

Hoe test ik foutafhandeling zonder echte fouten te triggeren?

Gebruik een mock van client.models.generate_content die een errors.APIError met een gekozen code gooit, of een respons met een block_reason. Test elk pad apart: 429-retry, 403-stop, prompt-blokkade en output-blokkade. Tools voor chaos testing kunnen willekeurig fouten injecteren om de robuustheid onder load te toetsen.

Moet ik alle API-fouten aan de gebruiker tonen?

Nee. Log alles intern, maar toon gebruikers alleen een bruikbare melding. Gebruik "Probeer het opnieuw" voor tijdelijke fouten, "Je vraag is geblokkeerd" voor safety en een neutrale "Er ging iets mis" voor interne fouten.