Traitement des erreurs et codes d'état
1) Pourquoi normaliser les erreurs
Un contrat d'erreur unique accélère le débogage des clients, réduit les fausses retraits et rend RCA reproductible. Bon système :- Codage prévisible du type de problème,
- donne au client des indices valables (que faire ensuite),
- protège contre les fuites des pièces internes,
- compatible avec les retraits et l'idempotence.
2) Principes de conception
1. Un schéma d'erreur pour tous les services (REST/GraphQL/gRPC/webhooks).
2. La sémantique claire des rétrogrades : quels sont les codes à retracer, lesquels ne le sont pas.
3. Fail-closed sur les opérations write : Mieux vaut 4xx/5xx que la non-consistance silencieuse.
4. Pas de fuites : ne pas révéler SQL, piles, fligs, ID internes.
5. Trace : retournez toujours 'trace _ id '/' correlation _ id'.
6. Localisation des messages - en option, mais les codes et 'reason' restent stables.
3) Format unique (Détails du problème/JSON)
Format de base recommandé (compatible avec la RFC 7807) :json
{
"type": "https://errors.example.com/auth/invalid-token",
"title": "Invalid access token",
"status": 401,
"code": "AUTH_INVALID_TOKEN",
"detail": "Token expired or signature invalid.",
"instance": "/api/v1/payments/12345",
"trace_id": "01HX3...ABC",
"hint": "Obtain a new token via OAuth2 refresh.",
"meta": {
"scope": "payments:write",
"policy": "deny-by-default"
}
}
Explications :
- 'type' est l'URL stable de la classe d'erreur.
- 'Code 'est un code de domaine machine court (stable entre les versions).
- 'hint' - Quoi faire au client (répétition, mise à jour du token, modification des paramètres).
- « meta » - détails sécurisés (pas de secrets et PII).
4) Carte des codes d'état (ensemble minimum)
Authentification/autorisation
400 Bad Request - Validation structurelle/schéma.
401 Non validé - Non/token non validé. Ajoutez 'WWW-Authenticate'.
403 Forbidden - authentifié, mais aucun droit/politique refusé.
404 Not Found - Masquer l'existence d'une ressource en l'absence de droits.
409 Conflict est un conflit de versions/états (lock optimiste, idempotence).
451 Unavailable For Legal Reasons - bloc de conformité/juridiction.
Limites et protection
408 Request Timeout - le client envoie le corps trop lentement.
409/425 Too Early - interdiction de la répétition précoce dans le 0-RTT/TLS 1. 3.
429 Too Many Requests - avec 'Retry-After' et la politique de limite.
499 Client Closed Request - (sur le périmètre/NGINX) le client a rompu la connexion.
Données et règles commerciales
422 Contenu irréductible - la validation d'entreprise a passé le schéma, mais le sens est faux.
423 Locked - ressource bloquée (KYC review, AML freeze).
409 Conflict - Double envoi, course, limite d'état (par exemple, « déjà en cours de traitement »).
410 Gone - endpoint/ressource enlevée (deprecate terminé).
De serveur
500 Internal Server Error - erreur inconnue ; ne pas révéler les détails.
502 Bad Gateway - La dépendance a renvoyé l'erreur/proxy.
503 Service Unavailable - Travaux de dégradation/planification ; ajouter « Retry-After ».
504 Gateway Timeout - temps de dépendance.
5) La sémantique des rétrogrades et de l'idempotence
Vous ne pouvez pas retracer : 400/ 401/403/404/422 (si le client n'a pas modifié la demande).
Peut être rétracté : 408/429/5xx/ 425/499/504 (avec backoff + jitter).
Idempotence : pour 'POST', activez 'Idempotency-Key' (UUIDv4).
En cas de conflit de réexécution, renvoyez 409 avec 'hint :' Use same Idempotency-Key or GET status '.
Ajoutez 'Idempotency-Replay : true' lorsque vous retournez le résultat enregistré.
HTTP/1.1 429 Too Many Requests
Retry-After: 3
RateLimit-Limit: 50
RateLimit-Remaining: 0
RateLimit-Reset: 1730641030
6) Validation d'entrée : structure des erreurs de champ
Pour 400/422, utilisez un tableau d'erreurs de champ :json
{
"type": "https://errors.example.com/validation",
"title": "Validation failed",
"status": 422,
"code": "VALIDATION_ERROR",
"trace_id": "01HX4...XYZ",
"errors": [
{"field": "amount", "rule": "min", "message": "Must be >= 10"},
{"field": "currency", "rule": "enum", "message": "Unsupported currency"}
]
}
7) Échecs partiels (batch/panne partielle)
Dans les endpoints batch, ne cachez pas les erreurs à l'intérieur de 200 sans structure. Renvoyez 207 Multi-Status ou 200 avec un tableau de résultats où chaque tâche a son propre statut :json
{
"status": "partial",
"succeeded": 8,
"failed": 2,
"results": [
{"id": "op1", "status": 201},
{"id": "op2", "status": 422, "error": {"code":"VALIDATION_ERROR","detail":"..."}}
]
}
8) Pagination et réponses « vides »
La collection vide est 200 avec 'items : []', pas 404.
La fin de la page est 'next _ page _ token'.
Le jeton incorrect est 400 avec 'code : PAGINATION_CURSOR_INVALID'.
9) Webhooks : livraison fiable
Signez les événements (HMAC) et vérifiez avant le traitement.
La réponse au traitement réussi est 2xx (meilleure que 204).
Échec temporaire du destinataire : 5xx ; l'expéditeur répète (backoff exponentiel, gitter).
Déduplication par 'event _ id' et enregistrement du résultat (idempotent consumer).
Payload non valide est 400/422 sans répétition.
10) Conformité aux protocoles (gRPC/GraphQL)
gRPC → HTTP:- `INVALID_ARGUMENT` → 400
- `UNAUTHENTICATED` → 401
- `PERMISSION_DENIED` → 403
- `NOT_FOUND` → 404
- `ALREADY_EXISTS` → 409
- `FAILED_PRECONDITION` → 412/422
- `RESOURCE_EXHAUSTED` → 429
- `ABORTED` → 409
- `UNAVAILABLE` → 503
- `DEADLINE_EXCEEDED` → 504
json
{
"data": { "createPayment": null },
"errors": [{
"message": "Forbidden",
"extensions": { "code": "FORBIDDEN", "status": 403, "trace_id": "..." },
"path": ["createPayment"]
}]
}
Il est recommandé d'utiliser le code HTTP correspondant plutôt que 200 pour les erreurs critiques.
11) Titres et conseils au client
« Retry-After » - secondes/NTTR-date (429/503/425/408).
« Warning » est une dégradation douce ou un deprecate (« 199 - Feature X is deprecated »).
`Deprecation`, `Sunset`, `Link: <...>; rel = « déprécation » - pour l'arrêt contrôlé.
« Problème-Type » : Routage rapide des erreurs sur le client.
"X-Trace-Id'/" Correlation-Id" - relie les logs/trajets.
12) Sécurité des messages
Ne répétez pas les secrets d'entrée (jetons/signatures) dans le corps de la réponse.
Masquer le PAN/PII ('1234').
Pour 401/403 - ne pas révéler exactement quel attribut a échoué.
Pour 404, au lieu de « ressources exists but not yours » - juste 404.
13) Observation des erreurs
Métriques :- `http_errors_total{status, route, tenant}`
- 'error _ classes _ total {code} '(par' code 'du corps)
- proportion 429, 5xx ; 'p95 '/' p99' latency pour les réponses erronées séparément
- 'retry _ after _ seconds _ bucket' est un histogramme de conseils de répétition
- associez la réponse à 'trace _ id', gardez' code ',' type ',' status ',' route ',' tenant ', sans PII.
- surbrillance '5xx _ rate> X %' à RPS> N ;
- une augmentation de 429 sur les itinéraires critiques ;
- 'timeout/504' chez les dépendances ;
- fréquentes 409/idempotence → signe de course.
14) Exemples
14. 1 422 (validation d'entreprise)
json
{
"type": "https://errors.example.com/payments/limit-exceeded",
"title": "Limit exceeded",
"status": 422,
"code": "PAYMENT_LIMIT_EXCEEDED",
"detail": "Daily withdrawal limit reached for KYC1.",
"hint": "Increase limits after KYC2 or try tomorrow.",
"trace_id": "01J5...XYZ"
}
14. 2 409 (idempotence)
HTTP/1.1 409 Conflict
Idempotency-Replay: true
json
{
"type": "https://errors.example.com/idempotency/replay",
"title": "Duplicate request",
"status": 409,
"code": "IDEMPOTENT_REPLAY",
"detail": "A request with the same Idempotency-Key was already processed.",
"hint": "Reuse the same Idempotency-Key and GET the operation status."
}
14. 3 429 (limites)
json
{
"type":"https://errors.example.com/rate/too-many-requests",
"title":"Too many requests",
"status":429,
"code":"RATE_LIMITED",
"detail":"Per-key rate limit exceeded.",
"hint":"Retry after the time specified in Retry-After header."
}
15) Anti-modèles
Renvoyer 200 avec le texte d'erreur dans le corps.
Mélanger différents formats d'erreur entre les services.
Afficher la pile/SQL/noms de table/URL interne dans 'detail'.
Utiliser 'message' au lieu de 'code '/' type' stable.
Renvoyer 500 en cas d'erreur professionnelle attendue (par exemple, « solde insuffisant »).
Sémantique incohérente entre REST/GraphQL/gRPC.
16) Spécificités d'iGaming/Finance
Codes clairs pour KYC/AML/sanctions : 'KYC _ REQUIRED', 'KYC _ REVIEW', 'AML _ LOCK', 'SANTÉ _ BLOCKED'.
Limites de compétence : 451 avec un libellé sécurisé sans liste.
Opérations d'écriture monétaire : 409/423 en compétition et en blocage, 'hint' avec fenêtre de répétition.
Invariants limites de joueur : utilisez le 422 pour enfreindre les règles de paiement responsable.
Audit : journaux de décision immuables (code, temps, actionneur, trace_id).
17) Chèque-liste prod-prêt
- Schéma d'erreur JSON unique, stable 'type '/' code'.
- Mapping HTTP ↔ gRPC/GraphQL est harmonisé et documenté.
- Sémantique des rétrogrades + « Retry-After » ; idempotence pour write.
- Masquage des IPI/secrets ; 404 pour cacher les ressources.
- Métriques d'erreurs et alertes ; corollation avec 'trace _ id'.
- Politiques de déprécation : 'Deprecation', 'Sunset', 'Link'.
- Tests : negative/fuzz, conflit de versions, chute des dépendances, double-submit.
- Hyde aux clients : exemples de back-offs et traitement 409/422/429/5xx.
18) TL; DR
Normalisez le format d'erreur JSON unique c 'type '/' code '/' trace _ id', utilisez des codes HTTP corrects, distinguez la validation (400/422), les droits de (401/403/404), les conflits/idempotentiités (409) et les limites (429). Laissez clair 'Retry-After' et 'hint', masquez les données sensibles, logiez les erreurs avec 'trace _ id'et construisez les alertes à 5xx/429/p99.