Aller au contenu principal

Gestion d'erreurs et codes HTTP

Gestion d’erreurs et codes HTTP

En production, les erreurs sont inévitables. Comprendre les codes HTTP retournés par l’API et savoir les gérer correctement est essentiel pour construire des applications robustes.

Les codes HTTP de l’API OpenAI

L’API retourne des codes HTTP standards pour indiquer le résultat de chaque requête :

from openai import OpenAI
from openai import (
    APIError,
    AuthenticationError,
    BadRequestError,
    RateLimitError,
    APIConnectionError,
    InternalServerError,
    PermissionDeniedError,
    NotFoundError,
    UnprocessableEntityError,
)

client = OpenAI()

401 — AuthenticationError

try:
    # Clé API invalide ou manquante
    bad_client = OpenAI(api_key="sk-invalid")
    bad_client.responses.create(
        model="gpt-5.3",
        input="Test"
    )
except AuthenticationError as e:
    print(f"Code : {e.status_code}")  # 401
    print(f"Message : {e.message}")
    # Action : vérifier la clé API, la régénérer si nécessaire

400 — BadRequestError

try:
    # Paramètres invalides
    client.responses.create(
        model="gpt-5.3",
        input="Test",
        temperature=5.0  # Valeur invalide (max 2.0)
    )
except BadRequestError as e:
    print(f"Code : {e.status_code}")  # 400
    print(f"Message : {e.message}")
    # Action : corriger les paramètres de la requête

429 — RateLimitError

try:
    # Trop de requêtes ou quota dépassé
    client.responses.create(
        model="gpt-5.3",
        input="Test"
    )
except RateLimitError as e:
    print(f"Code : {e.status_code}")  # 429
    print(f"Message : {e.message}")
    # Action : attendre et réessayer avec backoff exponentiel

500/503 — InternalServerError

try:
    client.responses.create(
        model="gpt-5.3",
        input="Test"
    )
except InternalServerError as e:
    print(f"Code : {e.status_code}")  # 500 ou 503
    print(f"Message : {e.message}")
    # Action : réessayer après un délai, l'erreur est côté OpenAI

Gestionnaire d’erreurs complet

Voici un pattern robuste pour gérer toutes les erreurs possibles :

from openai import OpenAI, APIError, AuthenticationError, RateLimitError
from openai import BadRequestError, APIConnectionError, InternalServerError
import time

client = OpenAI()

def appel_api_robuste(prompt: str, modele: str = "gpt-5.3",
                      max_retries: int = 3) -> str:
    """Appel API avec gestion complète des erreurs."""

    for tentative in range(max_retries):
        try:
            response = client.responses.create(
                model=modele,
                input=prompt
            )
            return response.output_text

        except AuthenticationError:
            # Ne pas réessayer — la clé est invalide
            raise RuntimeError("Clé API invalide. Vérifiez OPENAI_API_KEY.")

        except BadRequestError as e:
            # Ne pas réessayer — la requête est mal formée
            raise RuntimeError(f"Requête invalide : {e.message}")

        except RateLimitError:
            # Réessayer avec backoff exponentiel
            delai = 2 ** tentative
            print(f"Rate limit atteint. Attente {delai}s...")
            time.sleep(delai)

        except InternalServerError:
            # Réessayer — erreur côté serveur
            delai = 2 ** tentative
            print(f"Erreur serveur. Tentative {tentative + 1}/{max_retries}...")
            time.sleep(delai)

        except APIConnectionError:
            # Réessayer — problème réseau
            delai = 2 ** tentative
            print(f"Erreur connexion. Tentative {tentative + 1}/{max_retries}...")
            time.sleep(delai)

    raise RuntimeError(f"Échec après {max_retries} tentatives")

# Utilisation
try:
    resultat = appel_api_robuste("Bonjour !")
    print(resultat)
except RuntimeError as e:
    print(f"Erreur fatale : {e}")

Erreurs spécifiques à la Responses API

Contexte trop long

try:
    response = client.responses.create(
        model="gpt-5.3",
        input="x " * 300000  # Dépasse la fenêtre de contexte
    )
except BadRequestError as e:
    if "context_length_exceeded" in str(e.message):
        print("Le prompt est trop long pour ce modèle.")
        print("Solutions : tronquer le texte ou utiliser GPT-5.4 (1M tokens)")

Modèle indisponible

try:
    response = client.responses.create(
        model="gpt-4o",  # Modèle retiré !
        input="Test"
    )
except NotFoundError as e:
    print(f"Modèle non trouvé : {e.message}")
    print("Utilisez gpt-5.3 ou gpt-5.4 à la place.")

Logging structuré des erreurs

import logging
import json
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("openai_client")

def appel_avec_log(prompt: str) -> str:
    """Appel API avec logging structuré."""
    request_id = None

    try:
        response = client.responses.create(
            model="gpt-5.3",
            input=prompt
        )

        logger.info(json.dumps({
            "event": "api_success",
            "model": response.model,
            "tokens": response.usage.total_tokens,
            "response_id": response.id,
            "timestamp": datetime.now().isoformat()
        }))

        return response.output_text

    except APIError as e:
        logger.error(json.dumps({
            "event": "api_error",
            "status_code": e.status_code,
            "message": e.message,
            "timestamp": datetime.now().isoformat()
        }))
        raise

resultat = appel_avec_log("Bonjour !")

Tableau récapitulatif

CodeExceptionRéessayer ?Action
400BadRequestErrorNonCorriger la requête
401AuthenticationErrorNonVérifier la clé API
403PermissionDeniedErrorNonVérifier les permissions
404NotFoundErrorNonVérifier le modèle/endpoint
422UnprocessableEntityErrorNonCorriger le format
429RateLimitErrorOuiBackoff exponentiel
500InternalServerErrorOuiAttendre et réessayer
503InternalServerErrorOuiService temporairement indisponible

Points clés à retenir

  • Catégorisez les erreurs : réessayables (429, 500, 503) vs fatales (400, 401, 403)
  • Utilisez le backoff exponentiel pour les erreurs réessayables
  • Ne réessayez jamais les erreurs d’authentification ou de requête invalide
  • Loguez chaque erreur avec contexte (code, message, timestamp)
  • Le SDK gère automatiquement 2 retries — configurez max_retries si besoin