Pannes en production
erreur 529 overloaded_error de l'API Anthropic
Script de reproduction · anthropic-529-retry-budget.py · mis à jour le 2026-08-06
Si vous lisez qu'un 529 signifie que vous avez été rate-limité et devez ralentir, c'est le mauvais chiffre en tête. 429 est la limite de débit de votre compte. 529 est overloaded_error : l'API est temporairement à court de capacité, ce n'est pas une question de quota, et c'est une erreur sur laquelle on peut réessayer. La distinction importe, car les deux exigent des réponses opposées : un 429 dit de vous brider vous-même, un 529 dit de reculer et de rejouer exactement la même requête. Les pannes rapportées sur cette page ne sont presque jamais l'API qui est tombée. C'est un retry qui n'a pas eu lieu, un budget de retry trop petit pour la charge créée, ou un 529 mal étiqueté comme 429 et donc jamais traité.
Le symptôme
L'erreur, verbatim depuis un run rapporté :
API Error: 529 {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"},"request_id":"req_011CZbe5iJ3VQtjAKPLD26Y1"}
Lisez le type, pas le texte. Le statut HTTP est 529 et le error.type intérieur est overloaded_error. Cette paire est le diagnostic complet, et c'est elle qui distingue overloaded_error des deux erreurs avec lesquelles on la confond :
| Ce que vous voyez | Ce que ça signifie | Ce que ça vous demande |
|---|---|---|
429 rate_limit_error | Votre compte a dépassé le RPM/TPM. Porte un header retry-after | Réduisez votre propre débit ; respectez retry-after |
500 api_error | Faute générique côté serveur | Réessayez avec backoff |
529 overloaded_error | Le service est temporairement saturé. Pas votre quota | Backoff avec jitter et retry ; la requête est correcte |
Le chiffre est stable sur toutes les surfaces. Le même 529 overloaded_error arrive d'un POST /v1/messages brut, des SDK Python et TypeScript, d'une session Managed Agents, et de chaque framework construit dessus. C'est pourquoi la recherche de la chaîne retourne des résultats de dépôts que vous n'avez jamais utilisés.
Un piège que le message dissimule : dans les exceptions typées du SDK, 529 est un 5xx, donc il remonte comme InternalServerError (la même classe qu'un 500), pas comme RateLimitError. Si votre handler branche uniquement sur la classe d'exception, un 529 atterrit dans votre chemin d'erreur serveur générique. Branchez sur error.type == "overloaded_error" quand vous avez besoin de le traiter distinctement d'un simple 500.
Ce qui le déclenche
Un client qui n'a jamais réessayé, ou un wrapper qui a avalé le retry. C'est le cas le plus fréquent, et il vaut la peine d'être énoncé clairement car il inverse l'intuition : les SDK Python et TypeScript officiels retentent déjà les 529. La valeur par défaut est max_retries=2, qui retente 408/409/429/5xx et les erreurs de connexion avec exponential backoff, donc un appel SDK nu tente jusqu'à trois fois sur une surcharge avant de lever. Quand un rapport dit "aucun retry, aucun backoff, la session meurt immédiatement", le retry était désactivé, fixé à zéro, ou avalé par une couche enveloppant le SDK. Un framework d'agents tiers qui attrape l'exception et termine le tour va présenter un 529 de première tentative comme un échec définitif, alors que le SDK en dessous était prêt à réessayer.
Un budget de retry trop petit pour la charge créée. Deux retries suffisent pour une perturbation isolée. Pas pour une fenêtre de surcharge soutenue que vous alimentez vous-même. L'artefact de cette page modélise exactement ça : envoyer 64 requêtes vers un endpoint qui peut en servir 8 par tick, en donnant à chaque requête les trois tentatives par défaut du SDK, et 40 des 64 sont abandonnées comme 529 définitifs quelle que soit la forme du backoff. Le budget, pas la courbe de backoff, est la contrainte déterminante à cette largeur.
Le fan-out est le déclencheur que vous contrôlez réellement. Aucun éditeur ne publie son vrai taux de 529 sous charge, et cette page ne prétend pas le connaître. Ce que les cas rapportés montrent clairement, c'est quand ça se déclenche : spawns de sous-agents et fan-out parallèle. Un rapport montre deux sous-agents planificateurs consécutifs sur Opus qui retournent tous deux 529 avant de basculer vers un modèle plus petit. Un autre décrit un fan-out de six sous-agents en parallèle où un seul 529 cascade et perd le travail en vol sur l'ensemble du lot, atteignant même des sessions non liées sur d'autres machines. Quand un orchestrateur spawn N agents d'un coup, ce sont N requêtes quasi simultanées qui frappent la même capacité au même instant. Vous avez créé le pic.
Des retries synchronisés amplifient le pic. Un backoff sans jitter est pire qu'il n'y paraît. Chaque requête qui échoue sur le même tick retente sur le même tick ultérieur, donc la flotte se déplace en bloc et re-percute comme une vague. Dans l'artefact, l'exponential sans jitter prend 127 ticks et 288 tentatives totales pour écouler un burst que le full jitter éclaire en 14 ticks et 225 tentatives. Chaque tentative supplémentaire est davantage de charge renvoyée vers l'endpoint que vous attendez. Une retry storm et un retry convoy sont tous deux des échecs du même ingrédient manquant.
Le 529 est mal étiqueté ou jamais loggé. Dans un cas rapporté, le 529 s'affiche dans l'UI comme "Rate limited", ce qui se lit comme un problème de quota 429 et envoie l'opérateur au mauvais correctif, et le buffer d'erreur structuré revient vide parce que le 529 n'a jamais été rendu qu'en texte. Si votre télémétrie ne peut pas vous donner le type et le request_id des erreurs rencontrées, vous ne pouvez pas distinguer une surcharge d'une limitation de débit d'une panne, et chacune ressemble à "l'API est instable".
Distinguer les cas
Avant de toucher aux constantes de backoff, répondez à trois questions dans l'ordre.
Est-ce un 529 ou un 429 ? Lisez le statut et le error.type. Si c'est rate_limit_error, le correctif est de votre côté du compteur : vous dépassez votre RPM ou TPM, respectez retry-after et réduisez votre propre débit. Si c'est overloaded_error, aucune réduction de votre débit moyen ne change le fait qu'un burst a momentanément dépassé la capacité partagée. N'appliquez pas le remède du 429 à un 529.
Quelque chose retente-t-il déjà ? Trouvez combien de tentatives chaque requête échouée a réellement effectuées. Si la réponse est une, le retry est coupé ou un wrapper intercepte l'exception avant le retry propre du SDK, et votre travail est d'arrêter de l'avaler, pas de coder à la main une nouvelle boucle. Si la réponse est déjà deux ou trois et que vous voyez encore des abandons, le budget est trop petit pour votre charge et la section suivante s'applique.
Cela n'arrive-t-il qu'à fort fan-out ? Un 529 qui n'apparaît que quand votre orchestrateur spawn de nombreux agents à la fois, et jamais sur un appel séquentiel isolé, n'est pas l'API qui est peu fiable. C'est votre profil de concurrence. Cette distinction décide si vous corrigez la politique de retry, la concurrence, ou les deux. La même dynamique de coût sur le troisième mois se cache derrière, comme dans la boucle de récursion LangGraph : le travail qui fan-out multiplie les requêtes, et les requêtes multipliées sont ce qui fait basculer la capacité.
Le correctif
L'ordre est délibéré, car l'artefact montre que les leviers n'ont pas tous le même poids.
Augmentez d'abord le budget de retry. À trois tentatives, la forme du backoff est sans importance : dans l'artefact, backoff fixe, exponentiel et jitteré abandonnent tous les mêmes 40 sur 64. Le budget est la ligne de survie. Fixez max_retries à une valeur qui reflète combien de temps une fenêtre de surcharge peut raisonnablement durer pour votre trafic, pas les deux par défaut. Dans le modèle, passer de trois tentatives à huit est la différence entre abandonner 40 tâches et n'en abandonner aucune.
Puis ajoutez du full jitter. Une fois que le budget peut écouler le burst, le jitter est ce qui le rend économique. Le full jitter (sleep(random.uniform(0, base * 2 ** attempt))) écoule le même burst de 64 requêtes en le moins de tentatives totales, car il maintient les slots servis pleins à chaque tick au lieu d'arriver en vagues qui se percutent. Si vous enveloppez le retry du SDK avec le vôtre, le jitter est la seule chose que vous ne devez pas omettre.
Limitez la concurrence de votre fan-out. Le 529 le moins cher est celui que vous ne provoquez jamais. Mettez un sémaphore ou une queue devant les spawns de sous-agents pour libérer N requêtes sur une courte fenêtre plutôt que toutes d'un coup. Un burst de 64 qui arrive en huit vagues de huit ne déclenche jamais la surcharge que le burst unique déclenche. C'est le levier que la politique de retry ne peut pas atteindre, car il change le pic lui-même.
Traitez 529 distinctement de 429. Branchez sur error.type. Sur rate_limit_error, respectez retry-after et réduisez votre propre débit. Sur overloaded_error, réessayez avec backoff jitteré et, si ça persiste, envisagez de router le retry vers un modèle moins chargé (un modèle plus petit est souvent disponible quand le plus grand est saturé). Ne les rendez jamais l'un comme l'autre.
Rendez-le observable. Loggez le statut, le error.type, et le request_id pour chaque échec dans une télémétrie structurée, pas seulement dans l'UI. Vous ne pouvez pas budgéter une politique de retry contre un mode d'échec que vous ne pouvez pas compter.
Vérifier le correctif
Exécutez l'artefact et lisez les deux tables. Il ne nécessite ni clé, ni modèle, ni réseau, donc il isole la dynamique de retry du fournisseur entièrement. Confirmez que la forme de votre panne correspond : si vous abandonnez des requêtes à trois tentatives, augmenter le budget est le premier geste, et la table vous dit de combien.
Forcez votre budget de retry à un et confirmez que les abandons apparaissent. Cela prouve que votre chemin de retry est réellement câblé. C'est l'étape que les gens sautent, et c'est pourquoi la plupart des rapports "nous réessayons déjà" existent : le retry était configuré sur un objet que le chemin chaud n'a jamais utilisé. Si un budget de un ne change pas votre taux d'abandon, votre budget de huit n'a jamais été appliqué non plus.
Puis loggez le compteur de tentatives et le error.type pendant une semaine sous fan-out réel. Le chiffre à surveiller n'est pas le taux moyen de 529 ; c'est le taux d'abandon à votre pic de concurrence, car c'est là que vit le burst. Si le nombre maximum de tentatives par requête monte pendant que votre trafic est stable, vous avez une fenêtre de surcharge qui survit à votre budget, et vous élargissez le budget ou réduisez le fan-out avant qu'elle trouve votre plafond.
Sources
Chaque affirmation ici provient soit d'une exécution de l'artefact, soit d'un cas rapporté listé dans le champ sources de la page : le spawn de sous-agent avec 529 sans exponential backoff et ses request IDs, le cas où un 529 s'affiche comme "Rate limited" et n'est jamais écrit dans le buffer d'erreur structuré pendant qu'un fan-out de six agents perd le travail en vol, le 529 transitoire qui abandonne des tâches longues sans récupération automatique, le 529 persistant retenté silencieusement sans sortie, les threads de retry sur 529 répétés, la demande d'auto-retry en mode interactif, le rapport de bug 529 brut, et un framework tiers qui termine la conversation sur overloaded_error au lieu de réessayer.
Les valeurs par défaut de retry du SDK (max_retries=2, retentant 408/409/429/5xx et les erreurs de connexion avec exponential backoff) et la distinction 429 / 529 / 500 proviennent de la référence API Anthropic sur les erreurs et la configuration client. Les comptages d'abandons et de tentatives sont produits par l'artefact de cette page, qui modélise la dynamique de retry d'un fan-out contre un endpoint à capacité limitée. Il ne mesure pas la capacité réelle d'Anthropic ni son taux réel de 529, et cette page non plus.
Cas rapportés
- github.com/anthropics/claude-code/issues/41624
- github.com/anthropics/claude-code/issues/68502
- github.com/anthropics/claude-code/issues/60577
- github.com/anthropics/claude-code/issues/45674
- github.com/anthropics/claude-code/issues/4054
- github.com/anthropics/claude-code/issues/35801
- github.com/anthropics/claude-code/issues/45066
- github.com/charmbracelet/crush/issues/960
Laissez votre email, on revient vers vous.