Aller au contenu
Gestion des erreurs

Gestion des erreurs

Une requête qui échoue renvoie un code de statut HTTP et un corps JSON avec un type d'erreur et un message. Cette page liste chaque statut, montre le corps d'erreur de chaque format d'API et indique quelles erreurs valent la peine d'être renvoyées.

Les SDK lèvent un objet d'erreur qui porte le code de statut et les champs du corps. Cette requête nomme un modèle qui n'existe pas :

import openai
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com/v1",
)

try:
    client.chat.completions.create(
        model="no-such-model",
        messages=[{"role": "user", "content": "Hello"}],
    )
except openai.APIStatusError as error:
    print(error.status_code)                           # 400
    print(error.type)                                  # invalid_request_error
    print(error.body["message"])                       # unknown model: no-such-model
    print(error.response.headers.get("x-request-id"))  # the id of this request

Elle renvoie le statut 400 et ce corps :

400 JSON
{
  "error": {
    "type": "invalid_request_error",
    "message": "unknown model: no-such-model"
  }
}

Corps de l'erreur

Il y a deux corps. /v1/messages répond au format Anthropic ; tous les autres points de terminaison répondent au format OpenAI. Dans les deux, les détails sont dans error.type et error.message, de sorte qu'un seul lecteur sert tous les points de terminaison.

401 Format OpenAI
{
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key",
    "code": "invalid_api_key",
    "param": null
  }
}
401 Format Anthropic
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key"
  }
}
Champ Type Description
error.type string Le genre d'erreur. Voir Types d'erreur ci-dessous.
error.message string Une phrase pour une personne. Les textes des tableaux ci-dessous sont stables, hormis leurs parties variables, indiquées par <id>, <n> ou ….
error.code string | null Présent sur certaines erreurs du format OpenAI : invalid_api_key avec 401, request_too_large avec 413, context_length_exceeded quand une conversation est trop longue, sinon null ou absent. Décidez d'abord d'après le statut et error.type, et traitez code comme un complément.
error.param null Présent sur les mêmes erreurs que code. Toujours null.
type string Format Anthropic uniquement. Toujours error.

Types d'erreur

Type Statuts Description
invalid_request_error 400, 413, 415, 422 La requête elle-même est erronée : un champ, le corps, l'id du modèle ou une entrée que le modèle n'accepte pas. Modifiez la requête avant de la renvoyer.
authentication_error 401 La clé API est absente, inconnue ou révoquée.
not_found_error 404 Le chemin n'existe pas.
rate_limit_error 429 Le solde ne couvre pas la requête, la protection anti-flood l'a arrêtée, ou le modèle est occupé. Le message indique laquelle.
api_error 500, 502, 503, 504 La requête était correcte et la panne est de notre côté. Renvoyez-la.
server_error 500 La requête était correcte et la panne est de notre côté. Renvoyez-la.

Codes de statut

Statut Type Message Quand
400 invalid_request_error Failed to parse the request body as JSON: … Le corps n'est pas du JSON valide.
400 invalid_request_error unknown model: <id> model n'est pas un id publié. Sur /v1/responses, aussi quand model est absent.
400 invalid_request_error No user message provided Niveaux Shannon : la requête ne contient aucun texte utilisateur.
400 invalid_request_error <id> does not accept image input Une image a été envoyée à un modèle open-weight hébergé sans entrée image.
400 invalid_request_error audio and video input are not supported by any hosted model Une partie audio ou vidéo a été envoyée à un modèle open-weight hébergé.
400 invalid_request_error <id> does not accept response_format Une sortie structurée a été demandée à un modèle open-weight hébergé qui n'en a pas.
400 invalid_request_error <id> does not accept response_format json_schema; use json_object Un schéma JSON a été envoyé à un modèle open-weight hébergé qui n'accepte que json_object.
400 invalid_request_error unknown reasoning effort '<value>'; expected off, low, medium or high L'effort de raisonnement n'est pas l'une des valeurs listées. Modèles open-weight hébergés.
400 invalid_request_error count_tokens is available for the hosted open models; unknown model: <id> Le comptage de tokens a été demandé pour un modèle qui n'est pas un modèle open-weight hébergé.
401 authentication_error Missing authentication Aucune clé API n'a été envoyée.
401 authentication_error Invalid API key La clé est inconnue, a été remplacée par une plus récente, ou a été révoquée.
404 not_found_error Not Found Le chemin n'existe pas, par exemple /v1/embeddings.
405 api_error Method Not Allowed Le chemin existe mais pas avec cette méthode HTTP.
413 invalid_request_error Failed to buffer the request body: … Le corps de la requête dépasse 32 MiB.
415 invalid_request_error Expected request with `Content-Type: application/json` L'en-tête Content-Type est absent ou n'est pas application/json.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … Un champ obligatoire est absent, comme messages, ou un champ a un mauvais type JSON.
429 rate_limit_error Quota exceeded. Upgrade your plan at shannon-ai.com/plan La limite de sortie de la requête est supérieure à ce qui reste de votre solde.
429 rate_limit_error Too many requests. Retry in <n>s. Protection anti-flood : plus de 120 requêtes en une minute sur votre compte. <n> est le nombre de secondes jusqu'à la fin de la minute.
429 rate_limit_error Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan shannon-coder-1 sur /v1/chat/completions et /v1/messages : les appels Shannon Coder de votre forfait pour la fenêtre actuelle de 4 heures sont épuisés.
429 rate_limit_error Shannon routes are temporarily busy. Please retry. Le modèle est occupé en ce moment.
500 server_error The model backend failed to answer. Please retry. Le modèle n'a pas produit de réponse.
500 server_error An internal error occurred. Please retry. Une défaillance interne pendant la préparation de la requête.
502 api_error The model backend failed to answer. Please retry. Le modèle n'a pas produit de réponse. Famille Shannon 3 et modèles open-weight hébergés.
503 api_error Could not verify your quota right now. Please retry. Votre solde n'a pas pu être lu en ce moment.
503 api_error token counting is temporarily unavailable for this model Le comptage de tokens n'est pas disponible pour ce modèle en ce moment.
504 api_error upstream timed out Un modèle open-weight hébergé a dépassé la limite de temps d'une requête hors streaming.

Les vérifications s'exécutent dans cet ordre : clé API, corps de la requête, id du modèle, protection anti-flood, solde. Une requête avec une mauvaise clé et un mauvais corps renvoie donc 401. Une requête refusée avec un statut 4xx n'est pas facturée.

Solde et protection anti-flood

Chaque requête est servie de façon égale : il n'y a ni niveaux de débit ni quota API distinct. Deux choses peuvent tout de même répondre 429, toutes deux avec le type rate_limit_error. Le message les distingue.

Solde

Votre solde est le forfait du jour, qui est réinitialisé à 00:00 UTC, puis vos crédits achetés. Le chat et l'API puisent dans le même solde.

Chaque requête réserve d'abord sa limite de sortie : max_tokens, ou max_output_tokens sur /v1/responses, 4,096 si vous n'envoyez ni l'un ni l'autre. Quand la réponse est terminée, le montant réservé est remplacé par ce que la requête a utilisé. Si le montant ne tient pas dans le solde restant, la requête renvoie Quota exceeded avant que le modèle démarre.

Les requêtes qui s'exécutent en même temps réservent chacune leur propre montant. De nombreuses requêtes parallèles avec un max_tokens élevé peuvent donc épuiser le solde un instant, même si chaque réponse est courte. Un max_tokens plus bas réserve moins.

Protection anti-flood

La protection anti-flood autorise 120 requêtes par minute et par compte. Elle compte le compte, pas la clé, sur /v1/chat/completions, /v1/responses et /v1/messages ensemble. La minute commence avec votre première requête.

La requête numéro 121 dans cette minute renvoie Too many requests. Retry in <n>s. et ne coûte rien. Les requêtes refusées comptent aussi : un client qui renvoie sans attendre reste donc refusé jusqu'à la fin de la minute. Attendez les <n> secondes indiquées dans le message.

Les requêtes envoyées en parallèle ne sont pas refusées parce qu'elles sont parallèles. Elles attendent en file et reçoivent une réponse à tour de rôle.

GET /v1/models et les points de terminaison de comptage de tokens ne sont pas comptés.

Plus d'informations sur les limites et le solde, et où voir votre propre solde et votre utilisation : Limites et solde Clés et utilisation

Que réessayer

Ne renvoyez une requête que si la même requête peut réussir. Les SDK OpenAI et Anthropic réessaient déjà deux fois d'eux-mêmes les réponses 429 et 5xx ; réglez max_retries (maxRetries en JavaScript) pour changer cela.

Erreur Renvoyer ? Comment
400, 404, 405, 413, 415, 422 Non. Corrigez d'abord la requête. Le message nomme le champ, le modèle ou l'entrée qui est erroné.
401 Non. Utilisez une clé valide. Créez ou remplacez la clé sur la page Clés et utilisation.
429 Too many requests Oui, après l'attente. Attendez le nombre de secondes indiqué dans le message, puis envoyez la même requête.
429 Quota exceeded Pas en l'état. Baissez max_tokens, attendez la réinitialisation quotidienne à 00:00 UTC, ou ajoutez des crédits.
429 Shannon Coder call quota reached Oui, dans la fenêtre suivante. Les fenêtres démarrent toutes les 4 heures à partir de 00:00 UTC. Un autre modèle répond immédiatement.
429 … temporarily busy Oui. Attendez quelques secondes, puis plus longtemps à chaque nouvelle tentative.
500, 502, 503 Oui. Attendez 1 seconde, puis 2, 4 et 8. Arrêtez-vous après quelques tentatives et signalez l'id de la requête.
504 Oui. Envoyez-la avec stream à true : la limite de temps s'applique aux réponses hors streaming.

Une boucle de réessai qui suit ce tableau :

import re
import time

import openai
from openai import OpenAI

# max_retries=0: this loop does the retrying, the SDK does not add its own.
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1", max_retries=0)


def create_with_retry(attempts=5, **request):
    for attempt in range(attempts):
        try:
            return client.chat.completions.create(**request)
        except openai.RateLimitError as error:
            text = str(error)
            flood = re.search(r"Retry in (\d+)s", text)
            if flood:
                time.sleep(int(flood.group(1)))   # flood protection: wait out the minute
            elif "temporarily busy" in text:
                time.sleep(2 ** attempt)
            else:
                raise                             # balance: a wait of seconds does not help
        except (openai.InternalServerError, openai.APIConnectionError):
            time.sleep(2 ** attempt)              # 1, 2, 4, 8 seconds
    raise RuntimeError("the request failed after all attempts")


response = create_with_retry(
    model="shannon-3",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

Erreurs dans un flux

Un flux a le statut 200 dès son premier octet. Un échec après cela arrive comme un événement dans le flux : un chunk avec un membre error sur /v1/chat/completions, un événement error sur /v1/messages, un événement response.failed ou error sur /v1/responses. Les événements de clôture du flux suivent quand même. Considérez une telle réponse comme échouée et renvoyez la requête, comme pour un statut 5xx.

Une conversation plus longue que la fenêtre de contexte du modèle est signalée de cette façon, avec le type invalid_request_error et le code context_length_exceeded. Raccourcissez la conversation avant de la renvoyer.

Les trames exactes sont décrites avec le streaming. Diffusion en continu

Ids de requête

Chaque réponse, erreurs comprises, a un en-tête x-request-id. C'est la valeur que vous avez envoyée dans votre propre en-tête x-request-id, ou 12 caractères hexadécimaux si vous n'en avez envoyé aucune. Journalisez-la, et citez-la quand vous demandez de l'aide pour une requête.