L'API Doubao est fournie par Volcengine Ark. Sa forme d'interface est proche d'OpenAI, mais la sémantique des erreurs a ses particularités. Le cas le plus typique est le 404 : le service n'est pas tombé, le problème vient souvent de l'ID d'endpoint d'inférence, du nom de modèle, de la région ou du chemin. Le 429 est aussi souvent mal interprété : limitation de débit, concurrence, quota, impayé ou pack de ressources épuisé, avec des traitements totalement différents.

Fixez l'ordre de diagnostic en cinq étapes : lire le code HTTP, lire le code et le message du corps, récupérer le request ID, reproduire avec une requête minimale, puis seulement modifier les paramètres ou changer de modèle. Cet article détaille 401, 404, 400, 429, timeouts et 5xx, avec un exemple de traitement d'erreur et une liste de journaux. Modèles et facturation font foi sur la page Tarifs des modèles.

Remarque : Doubao, Volcengine Ark et Volcengine sont des marques de ByteDance. Cet article est purement technique et n'implique aucune affiliation, agence ou coopération avec ByteDance. Les codes d'erreur, règles de limitation et comportements d'interface peuvent évoluer ; la documentation officielle et la réponse réelle font foi.

1. Distinguer d'abord les deux niveaux d'erreur

Le code HTTP décrit le résultat au niveau du protocole ; le code, le type et le message du corps précisent la cause. Un même 401 peut signifier une clé absente, erronée, expirée ou sans droit sur le projet ; un même 429 peut signifier des requêtes trop rapides, une concurrence trop élevée, un quota épuisé ou un impayé. Si vous ne retenez que le statut, vous ne pouvez que deviner.

HTTP Signification courante Cas Doubao Reprise
401 Échec d'authentification Clé absente, mal formée, expirée ou liée à une autre région Non, corriger d'abord les identifiants
403 Pas de droit Clé sans droit sur ce modèle ou cet endpoint Non, vérifier les autorisations
404 Ressource inexistante ID d'endpoint, nom de modèle, chemin ou région incorrect Non, vérifier d'abord
400 Paramètre invalide Contexte trop long, max_tokens illégal, format d'image non pris en charge Non, corriger la requête
429 Limitation ou quota RPM, TPM, concurrence dépassés, impayé ou pack épuisé Selon la cause
500/502/503 Erreur serveur Anomalie amont, gigue de passerelle, indisponibilité brève Oui, avec backoff
Timeout Connexion ou lecture Réseau, file d'attente, entrée ou sortie trop longue Reprises limitées

Les noms de champs dépendent de la réponse réelle : certains sont dans error.code, d'autres à la racine. Le code ne doit pas supposer une structure unique.

2. 401 : quatre causes d'échec d'authentification

Un 401 n'a rien à voir avec la capacité du modèle ou le solde ; vérifiez la chaîne des identifiants :

  • L'en-tête est-il Authorization: Bearer <clé>, avec un espace après les deux-points ?
  • La clé est-elle correctement chargée depuis la variable d'environnement, sans guillemets, espaces ni saut de ligne ?
  • La clé a-t-elle été supprimée, renouvelée ou a-t-elle expiré ?
  • Le projet, la région et l'URL de base de la clé sont-ils cohérents ?

Une clé copiée depuis une fenêtre de chat contient facilement des caractères invisibles. Envoyez d'abord une requête minimale qui ne produit qu'un mot : elle élimine la plupart des erreurs de base. Si la même clé fonctionne dans la console mais renvoie 401 dans le code, vérifiez la variable d'environnement et le proxy inverse, qui peut supprimer l'en-tête d'authentification.

3. 404 : ID d'endpoint et nom de modèle, la confusion la plus fréquente

Volcengine Ark connaît deux façons courantes de désigner un modèle : l'endpoint d'inférence créé dans la console, généralement préfixé par ep-, et l'appel direct par ID de modèle, pris en charge par certains modèles. Les deux se placent dans le champ model, mais les droits, la limitation et le périmètre peuvent différer. Mélanger ID d'endpoint et nom de modèle, ou copier l'endpoint d'un autre projet, produit un 404 ou une erreur « ressource inexistante ».

Ordre de vérification :

  1. Confirmez que l'URL de base est bien une adresse régionale du type https://ark.cn-beijing.volces.com/api/v3, sans /api/v3 en trop ou manquant.
  2. Vérifiez dans la console que l'endpoint existe, appartient au projet courant et n'est pas désactivé.
  3. Confirmez si le mode d'appel exige un ID d'endpoint ou un ID de modèle ; ne mélangez pas de mémoire.
  4. Envoyez une requête minimale avec curl pour écarter l'assemblage automatique du SDK et la réécriture du proxy.
  5. Regardez le type de contenu : une page d'erreur HTML suggère que la requête n'a pas atteint Ark, mais a été interceptée par une passerelle ou un proxy.

Un 404 avec HTML indique généralement un problème de chemin ou de proxy ; un 404 avec corps JSON ressemble davantage à un problème de modèle ou d'endpoint.

4. 400 : contexte trop long et paramètres invalides

Le 400 couvre un large spectre, le cas le plus fréquent étant le dépassement de contexte. La fenêtre est généralement calculée en tokens d'entrée plus tokens de sortie ; un max_tokens trop élevé fait déborder une requête qui passait auparavant. Les images, définitions d'outils et messages historiques consomment aussi des tokens.

La solution n'est pas d'augmenter la limite, mais de laisser une marge à l'entrée :

  • Comprimer ou segmenter le prompt système, supprimer les règles répétées.
  • Résumer l'historique en ne gardant que les derniers tours et les faits clés.
  • Pour un document long, rechercher puis assembler, sans insérer tout le texte.
  • Comprimer les images et vérifier que format et taille sont pris en charge.
  • Vérifier que temperature et top_p restent dans les bornes et que les rôles des messages sont valides.

Le comptage de tokens est une estimation : à l'approche de la limite, gardez 10 à 20 % de marge.

5. 429 : traiter séparément limitation, concurrence et quota

Un 429 a quatre causes :

  • Limitation de débit : requêtes ou tokens par minute au-delà de l'offre ; le service se rétablit généralement après backoff.
  • Limitation de concurrence : trop de requêtes simultanées ; réduisez la concurrence ou mettez en file, car réessayer aggrave la congestion.
  • Quota ou pack épuisé : réessayer ne sert à rien, il faut recharger ou ajuster l'offre.
  • Statut de compte anormal : impayé, identité non vérifiée ou changement de droits ; suivez les indications de la console.

Si la réponse contient Retry-After, attendez ce délai ; sinon, appliquez un backoff exponentiel avec gigue aléatoire. Ne réessayez pas les 429 de quota ou d'impayé : cela prolonge l'incident. Un 429 en streaming peut survenir à l'établissement de la connexion ou comme événement d'erreur dans le flux. Le client doit traiter à la fois le statut et les erreurs internes au flux, sans se fier au premier segment.

6. Timeouts et coupures de flux : localiser en trois segments

Un timeout se décompose en trois :

  • Timeout de connexion : DNS, TLS, proxy de sortie ou réseau injoignable ; l'échec survient généralement en quelques secondes.
  • Timeout du premier token : la requête a atteint l'amont, mais fait la queue ou traite une entrée longue, d'où l'absence prolongée du premier fragment.
  • Timeout de lecture : l'intervalle entre deux fragments est trop long, à cause d'une sortie longue, d'une inférence prolongée ou d'une connexion coupée par un équipement intermédiaire.

Les trois nécessitent des configurations distinctes. Un timeout global trop grand immobilise le pool de connexions sur les requêtes en échec ; trop petit, il tue des sorties longues légitimes. Prévoyez environ 5 secondes pour la connexion, un timeout de lecture calé sur le P95 métier, et un budget total au niveau applicatif.

En streaming, vérifiez aussi le tampon du proxy inverse : il retarde les fragments et donne l'impression d'un blocage. Après une coupure, ne stockez pas un contenu partiel comme résultat complet.

7. Request ID et journaux : les preuves du diagnostic

Les réponses de Volcengine Ark portent généralement un identifiant de requête, souvent X-Request-Id ; certaines interfaces renvoient aussi un identifiant de journal du type X-Tt-Logid. Le nom exact dépend de la réponse : conservez l'ensemble des en-têtes plutôt qu'un seul champ.

Le client doit aussi générer son propre trace_id, stable sur toutes les reprises d'une même requête métier. Le journal doit contenir au minimum : heure, trace_id, URL de base, modèle ou endpoint, code de statut, code et message d'erreur, durée, nombre de tentatives, caractère streaming et consommation de tokens.

Pour la clé, n'enregistrez que les premiers caractères ou un hachage ; le texte utilisateur et la réponse complète ne doivent pas être écrits en entier sur disque. Limitez le corps d'erreur conservé aux 500 premiers caractères.

8. Reprises et backoff : un exemple utilisable

Le code ci-dessous désactive les reprises automatiques du SDK, décide selon le statut et le contenu de l'erreur, respecte Retry-After et génère un trace_id par requête logique :

import os
import random
import time
import uuid

import httpx
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError

client = OpenAI(
    api_key=os.environ["ARK_API_KEY"],
    base_url=os.environ.get(
        "ARK_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"
    ),
    timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=10.0),
    max_retries=0,  # désactive les reprises du SDK, gérées ci-dessous
)

def parse_error(exc: APIStatusError) -> tuple[str, str]:
    try:
        body = exc.response.json()
    except Exception:
        return "", exc.response.text[:200]
    err = body.get("error", body)
    return str(err.get("code") or err.get("type") or ""), str(err.get("message") or "")

def is_retryable(exc: Exception) -> bool:
    if isinstance(exc, (APITimeoutError, APIConnectionError)):
        return True
    if not isinstance(exc, APIStatusError):
        return False
    if exc.status_code >= 500:
        return True
    if exc.status_code != 429:
        return False
    code, message = parse_error(exc)
    text = f"{code} {message}".lower()
    # un 429 de quota, d'impayé ou de droits ne se réessaie pas ; complétez les mots-clés selon les messages réels
    return not any(k in text for k in ("quota", "arrears", "balance", "permission"))

def ask(prompt: str, max_attempts: int = 4) -> str:
    trace_id = uuid.uuid4().hex[:12]
    for attempt in range(1, max_attempts + 1):
        try:
            resp = client.chat.completions.create(
                model=os.environ["ARK_MODEL"],  # ID d'endpoint ou ID de modèle
                messages=[{"role": "user", "content": prompt}],
                max_tokens=1024,
            )
            return resp.choices[0].message.content
        except Exception as exc:
            status = getattr(exc, "status_code", "")
            code, message = parse_error(exc) if isinstance(exc, APIStatusError) else ("", "")
            print(
                f"trace={trace_id} attempt={attempt} status={status} "
                f"code={code} message={message[:200]}"
            )
            if attempt == max_attempts or not is_retryable(exc):
                raise
            retry_after = getattr(
                getattr(exc, "response", None), "headers", {}
            ).get("retry-after")
            try:
                wait = float(retry_after) if retry_after else min(2 ** (attempt - 1), 20)
            except ValueError:
                wait = min(2 ** (attempt - 1), 20)
            time.sleep(wait + random.uniform(0, 1))
    raise RuntimeError("Nombre de tentatives épuisé")

Trois règles complémentaires : pour les requêtes à effet de bord, vérifiez l'état avec une clé d'idempotence ; contrôlez globalement le budget de reprises ; surveillez la part de 429 et la latence P95, et réduisez d'abord la concurrence en cas de hausse.

9. Liste à préparer avant de contacter le support

  1. Heure et fuseau de l'incident, caractère continu ou intermittent.
  2. URL de base, chemin complet, nom de modèle ou ID d'endpoint.
  3. Code HTTP, code d'erreur, message et request ID.
  4. Une requête curl minimale reproductible, clé masquée.
  5. Streaming ou non, contexte long, images ou appels d'outils.
  6. Modification récente de la clé, de l'endpoint, du quota ou du proxy.
  7. Périmètre d'impact : une clé, un projet, une région ou tous les services.

Avec ces éléments, le support peut comparer directement aux journaux amont. Après chaque incident, ajoutez une supervision ou une alerte.

10. Questions fréquentes

Un 404 signifie-t-il toujours que le modèle est retiré ?

Non. Le plus souvent, l'ID d'endpoint ou le nom de modèle est incorrect, l'endpoint a été supprimé, la région ne correspond pas, ou le chemin n'a pas /api/v3. Regardez d'abord si la réponse est du JSON ou une page HTML, puis vérifiez chaque point.

Combien de fois réessayer un 429 ?

Pour une limitation de débit ou une congestion brève, deux à trois reprises avec backoff exponentiel. Ne réessayez pas un 429 dû à un quota épuisé, un impayé ou des droits insuffisants : traitez d'abord le compte ou l'offre. Respectez Retry-After.

Comment se calcule la longueur de contexte ?

Généralement en tokens d'entrée plus tokens de sortie ; max_tokens consomme aussi l'enveloppe totale. Images, définitions d'outils et messages historiques comptent en entrée : gardez une marge près de la limite.

Comment régler les timeouts en streaming ?

Le timeout de lecture borne généralement l'intervalle entre deux fragments, pas la durée totale. Le timeout de connexion se règle séparément, puis l'application ajoute un budget total et un timeout d'inactivité. Le tampon du proxy retarde l'envoi des fragments.

Les erreurs augmentent après l'ajout d'une passerelle : que faire ?

Déterminez d'abord si l'erreur vient de la passerelle ou de l'amont : format du corps, code de statut et request ID. Conservez le trace ID et comparez une requête minimale en direct et via la passerelle. La plupart des problèmes viennent de l'assemblage du chemin, du transfert de l'en-tête d'authentification ou du timeout.

Pour aller plus loin

Créez d'abord une clé de test sur ZSQH AI Gateway, entraînez-vous une fois sur 401, 404, 429 et timeout, puis branchez la supervision de production. Les modèles disponibles et la facturation figurent sur la page Tarifs des modèles.

Articles liés :