En cas de problème
Rechercher une erreur
#Coller l'erreur
Lisez les erreurs dans cet ordre : statut HTTP, puis error.type, puis error.code. Le statut est toujours significatif ; type indique qui a rejeté l'appel ; code n'est stable que pour les erreurs émises par la passerelle elle-même.
#Comment réagir
- 1
Commencez par le statut
402 : rechargez ; 403 : portée de la clé ; 429 : attendez ; 5xx : réessayez. Cette couche ne ment jamais et ne change jamais.
- 2
Vérifiez error.type
xiaoxiang_error est le nôtre. Tout le reste — openai_error, claude_error, gemini_error — est le corps propre au fournisseur, transmis tel quel.
- 3
Puis lisez error.code
Ne vous basez sur code que pour xiaoxiang_error. Les codes en amont appartiennent au fournisseur et peuvent changer sans préavis.
Toutes les erreurs n'ont pas de corps JSON
Enveloppes d'erreur
Cinq formats atteignent votre client. Gérez-les tous les cinq — les trois derniers sont ceux que les intégrations oublient le plus souvent.
Erreur de la passerelle
La passerelle elle-même a rejeté l'appel — authentification, quota, routage, format de la requête.
{
"error": {
"message": "insufficient account balance, please top up to continue (request id: 20260910...)",
"type": "xiaoxiang_error",
"code": "insufficient_user_quota"
}
}Erreur en amont, transmise telle quelle
Le fournisseur a répondu avec sa propre erreur. Le corps est transmis sans modification : type et code sont ceux du fournisseur.
{
"error": {
"message": "This model's maximum context length is 128000 tokens.",
"type": "invalid_request_error",
"code": "context_length_exceeded"
}
}Rejet par un garde-fou
L'idempotence et quelques autres garde-fous préalables répondent avant que l'enveloppe du relais n'existe. Format plat, code en majuscules.
{
"code": "IDEMPOTENCY_IN_PROGRESS",
"error": "a request with this Idempotency-Key is still processing"
}Rejet de tâche asynchrone
Chaque endpoint de tâche — vidéo, tâche d'image, musique, 3D — signale les échecs dans son propre format plat. Il n'y a pas d'objet error imbriqué ici : un parseur écrit pour /v1/chat/completions lira null.
{
"code": "task_not_exist",
"message": "task does not exist",
"data": null
}Statut seul, corps vide
Les rejets pour limite de débit et les corps de requête trop volumineux s'interrompent avec un simple statut.
HTTP/1.1 429 Too Many Requests
Retry-After: 20
Content-Length: 0En-têtes de réponse
Journalisez l'ID de requête de chaque réponse, pas seulement des échecs. C'est l'identifiant qui permet au support de retracer un appel, et il fait la différence entre une réponse en cinq minutes et une journée de suppositions.
| En-tête | Envoyé quand | Utilisation |
|---|---|---|
| X-Oneapi-Request-Id | Chaque réponse, succès ou erreur. | Enregistrez-le avec votre propre identifiant de trace. Le support traite les tickets à partir de cette valeur, et ce même identifiant est ajouté aux messages d'erreur de la passerelle. |
| Retry-After | En cas de 429, et de 409 tant qu'une requête avec la même Idempotency-Key est encore en cours. | Attendez au moins ce nombre de secondes. C'est la seule indication de backoff envoyée par la passerelle. |
| X-Idempotent-Replayed | La réponse stockée est rejouée lorsqu'une Idempotency-Key est réutilisée. | true signifie qu'aucun nouveau traitement n'a eu lieu et que rien n'a été refacturé — traitez le corps comme le résultat d'origine. |
| X-Idempotency-Sealed | La réponse d'origine ne peut pas être rejouée sans risque (par exemple un stream ou un corps trop volumineux). | Cessez de réessayer avec cette clé. Faites plutôt le rapprochement via l'endpoint de la tâche ou vos propres enregistrements. |
const response = await fetch(url, init);
const requestId = response.headers.get("X-Oneapi-Request-Id");
if (!response.ok) {
// Bodies are absent on 429 and 413 — never assume JSON here.
const detail = await response.text();
logger.error({ status: response.status, requestId, detail });
throw new GatewayError(response.status, requestId, detail);
}#Table des codes d'erreur
Erreurs émises par la passerelle elle-même, toujours avec le type "xiaoxiang_error". Deux lignes partagent le statut 429 avec des sens opposés — lisez le code avant d'appliquer un backoff.
| HTTP | Code | Signification | Que faire | Réessayer ? |
|---|---|---|---|---|
| 401 | aucun | Jeton bearer absent, mal formé, supprimé ou désactivé. Cette couche ne définit aucun code — basez-vous sur le statut. | Vérifiez l'en-tête Authorization et l'état de la clé dans Console → Clés API. | non |
| 401 | signature_required | La signature des requêtes est activée sur la clé et la requête ne contient pas X-Timestamp, X-Nonce ou X-Signature. | Signez chaque requête effectuée avec cette clé (Authentification et clés → Signature des requêtes), ou désactivez la signature sur la clé. | non |
| 401 | signature_expired | X-Timestamp n'est pas en secondes Unix sur 10 chiffres ou s'écarte de plus de 5 minutes de l'heure du serveur. La réponse contient X-Server-Timestamp. | Corrigez l'horloge du client de l'écart indiqué par X-Server-Timestamp et signez de nouveau avec un nouveau nonce. | oui |
| 401 | signature_invalid | X-Signature ne correspond pas au secret de signature de la clé, ou X-Nonce est mal formé. | Signez l'horodatage, le nonce, la méthode et le chemin (sans paramètres de requête), joints par des sauts de ligne, avec le secret actuel indiqué dans Console → Clés API. | non |
| 401 | signature_replayed | Ce X-Nonce a déjà été utilisé avec cette clé au cours des 10 dernières minutes. | Générez un nouveau nonce aléatoire pour chaque requête, y compris les nouvelles tentatives. | oui |
| 403 | access_denied | L'IP de l'appelant ne figure pas dans la liste d'IP autorisées de la clé. | Ajoutez l'adresse de sortie dans Console → Clés API, ou supprimez la liste d'autorisation pour les clés sans IP fixe. | non |
| 403 | aucun | Compte suspendu, ou la clé n'est pas autorisée à utiliser ce modèle ou ce canal. | Vérifiez d'abord la liste des modèles autorisés de la clé ; si elle n'est pas restreinte, ouvrez un ticket — c'est le compte lui-même qui est bloqué. | non |
| 400 | unsupported_endpoint | Le modèle existe mais ne prend pas en charge ce chemin. Le message liste les endpoints qu'il prend en charge. | Déplacez l'appel vers un endpoint listé — les modèles de tâches d'image ne répondent pas sur /v1/chat/completions. | non |
| 400 | read_request_body_failed | Le corps de la requête n'a pas pu être lu — envoi tronqué, Content-Type incorrect ou flux interrompu prématurément. | Envoyez un corps complet avec un Content-Type correspondant. Renvoyer la même requête défectueuse échouera de la même façon. | non |
| 402 | insufficient_user_quota | Le solde du portefeuille ne couvre pas le montant préréservé pour cet appel. | Rechargez dans Console → Portefeuille, puis réessayez. | non |
| 429 | insufficient_user_quota | Le plafond de dépenses quotidien du compte est atteint. Même statut que la limitation de débit, sens opposé — le backoff ne le débloquera pas. | Augmentez ou supprimez le plafond dans les paramètres du compte, ou reprenez demain. Basez-vous sur le code, pas sur le seul 429. | non |
| 429 | aucun | Limitation RPM/RPD par clé ou garde-fou de concurrence. Le corps est vide ; Retry-After indique le délai d'attente. | Attendez Retry-After secondes, puis réessayez avec un backoff exponentiel et du jitter. | oui |
| 413 | read_request_body_failed | Le corps de la requête dépasse la taille acceptée. La limite au niveau transport interrompt la requête avec un corps vide. | Réduisez la charge utile — envoyez les fichiers via /v1/media/upload-policy et transmettez l'URL au lieu du Base64. | non |
| 422 | invalid_request | Aucun canal ne prend en charge ce mode de génération pour le modèle — par exemple image vers vidéo sur une route uniquement texte vers vidéo. | Consultez la fiche du modèle pour les modes pris en charge, puis changez de modèle ou de mode. | non |
| 503 | model_not_found | Nom de modèle inconnu, ou aucun canal opérationnel ne le sert actuellement. Attention au statut : un modèle inconnu renvoie 503, pas 404. | Vérifiez le nom avec /v1/models, puis réessayez — le basculement trouve généralement un canal opérationnel à la tentative suivante. | oui |
| 500 | sensitive_words_detected | Le prompt a été bloqué par le filtre de contenu avant de quitter la passerelle. | Modifiez le prompt. Réessayer avec le même texte échoue systématiquement. | non |
| 500 | count_token_failed | La passerelle n'a pas pu estimer les tokens pour la tarification, généralement à cause d'un tableau de messages mal formé. | Validez le tableau messages ; s'il est bien formé, réessayez et ouvrez un ticket avec l'ID de requête. | oui |
| 500 | invalid_request | La requête n'a pas pu être interprétée dans un format accepté par cet endpoint. Attention au statut — un corps illisible par la passerelle est signalé en 500, pas en 400. | Validez le JSON par rapport à la référence de l'endpoint avant de réessayer. | non |
| 502 | do_request_failed | Le fournisseur était injoignable, ou a renvoyé un échec que la passerelle n'a pas pu rattraper par basculement. | Réessayez avec backoff — la tentative suivante passe généralement par un autre canal. | oui |
| 504 | do_request_failed | Le fournisseur n'a pas répondu dans le délai imparti par la passerelle. | Réessayez avec backoff ; les prompts longs et les fournisseurs saturés expirent plus souvent. | oui |
| 499 | do_request_failed | Votre client a fermé la connexion avant l'arrivée de la réponse — généralement votre propre délai d'expiration HTTP qui se déclenche en plein stream. | Augmentez le délai de lecture du client. Les longues générations prennent plusieurs minutes, bien plus que la valeur par défaut de la plupart des bibliothèques HTTP. | non |
Erreurs du garde-fou d'idempotence
Elles proviennent du garde-fou de rejeu placé devant le relais : elles utilisent donc le format plat {code, error} avec un code en majuscules au lieu de l'enveloppe OpenAI.
| HTTP | Code | Signification | Que faire | Réessayer ? |
|---|---|---|---|---|
| 400 | IDEMPOTENCY_STREAM_UNSUPPORTED | Une Idempotency-Key a été envoyée sur une requête en streaming. | Retirez l'en-tête pour les appels en streaming, ou définissez stream:false si vous avez besoin de la protection par rejeu. | non |
| 400 | IDEMPOTENCY_BODY_UNREADABLE | Le corps n'a pas pu être haché pour le registre d'idempotence. | Envoyez un corps JSON ou multipart classique — les corps chunked qui ne peuvent pas être mis en mémoire tampon sont rejetés. | non |
| 409 | IDEMPOTENCY_PAYLOAD_CONFLICT | Cette clé a déjà été utilisée avec un corps, un chemin ou des paramètres de requête différents. | Générez une nouvelle clé. Une clé identifie une requête précise, pas une boucle de tentatives. | non |
| 409 | IDEMPOTENCY_IN_PROGRESS | La première requête avec cette clé est toujours en cours. | Attendez Retry-After (2 s) et réessayez avec la même clé — vous obtiendrez la réponse d'origine. | oui |
| 409 | IDEMPOTENCY_RESULT_UNREPLAYABLE | La requête d'origine est terminée, mais sa réponse a été scellée et ne peut pas être rejouée. | Ne renvoyez pas la requête à l'aveugle. Faites d'abord le rapprochement via l'endpoint de la tâche ou vos propres enregistrements. | non |
| 503 | IDEMPOTENCY_UNAVAILABLE | Le registre d'idempotence est indisponible : le garde-fou refuse la requête plutôt que de risquer un double débit. | Réessayez avec backoff — la requête n'a pas été exécutée. | oui |
Erreurs de tâches asynchrones
Les endpoints de vidéo, de tâche d'image, de musique et 3D répondent avec leur propre format plat — {code, message, data} — pour la soumission, l'interrogation comme l'annulation. Une particularité à prévoir : interroger un task id inconnu renvoie 400, l'annuler renvoie 404.
| HTTP | Code | Signification | Que faire | Réessayer ? |
|---|---|---|---|---|
| 400 | task_not_exist | Interrogation ou remix d'un task id inexistant ou appartenant à un autre compte. Attention au statut — interroger une tâche inconnue renvoie 400, alors que l'annuler renvoie 404. | Enregistrez task_id lors de la soumission et interrogez avec exactement cette valeur. | non |
| 400 | invalid_request | Un champ obligatoire est manquant — prompt pour un remix, task_id pour une récupération. | Corrigez le corps ou le chemin avant de réessayer. | non |
| 400 | task_channel_disable | Une tâche dérivée (remix, extension) est liée au canal de la tâche d'origine, et ce canal est désormais désactivé. | Soumettez une nouvelle tâche au lieu de dériver de l'ancienne. | non |
| 400 | invalid_source_task | La tâche source n'est pas du bon type pour cette opération — par exemple un id vidéo passé à une continuation 3D. | Vérifiez que l'id source provient de la même famille de capacités. | non |
| 404 | task_not_exist | Annulation d'un task id inexistant ou qui ne vous appartient pas. | Rien à annuler — considérez qu'elle n'existe plus. | non |
| 409 | task_not_cancellable | La tâche a déjà atteint un état final : il n'y a rien à annuler. | Interrogez une dernière fois et utilisez le résultat déjà payé. | non |
| 501 | task_cancel_not_supported | Le fournisseur derrière cette tâche n'expose aucune API d'annulation. C'est le cas de la plupart — l'annulation est l'exception, pas la règle. | Laissez la tâche se terminer. Concevez l'interface pour que l'annulation soit une tentative, pas une garantie. | non |
| 502 | cancel_task_failed | Le fournisseur était injoignable pendant l'annulation. La tâche est inchangée. | Réessayez une fois, puis laissez-la se terminer. | oui |
Toujours bloqué ?
Envoyez-nous l'ID de requête ou le message d'erreur, et nous examinerons le problème.