Erreurs — Codes HTTP
Format d'erreur unifié, codes HTTP 400 à 5xx, causes et stratégies de gestion pour une Wakapp robuste.
Erreurs — Codes HTTP
L'API WakaStart utilise un format d'erreur unifié (NestJS standard). Chaque code HTTP a une sémantique précise.
Format d'erreur unifié
json{ "statusCode": 403, "message": "Niveau d'administration insuffisant. Requis: CustomerAdmin, Actuel: User", "error": "Forbidden" }
Pour les erreurs de validation (400) avec plusieurs champs invalides :
json{ "statusCode": 400, "message": [ "email must be an email", "firstName should not be empty", "profileId must be a UUID" ], "error": "Bad Request" }
Champs :
| Champ | Type | Description |
|---|---|---|
statusCode | number | Code HTTP |
message | string | string[] | Description lisible du problème |
error | string | Libellé HTTP standard |
400 — Bad Request
Données invalides. Le body n'a pas passé la validation DTO.
Causes fréquentes :
- Email non valide
- Email de domaine jetable (yopmail, mailinator…)
- UUID manquant ou malformé
- Champ obligatoire absent
- Valeur hors enum (
teamLeveldoit êtreNONE | VIEWER | CONTRIBUTOR | MEMBER | MANAGER | ADMIN) - Token d'invitation invalide/expiré/déjà utilisé (lors de
/accept)
Action : Corriger le body de la requête et retenter.
401 — Unauthorized
Authentification manquante ou invalide.
Cas 1 — Token enrichi manquant
json{ "statusCode": 401, "message": "Token enrichi manquant ou invalide", "error": "Unauthorized" }
Causes :
- Header
x-enriched-tokenabsent — vérifier le proxy serveur - Cookie
wakastart_tokennon posé (l'appel à/api/auth/enricha échoué) - Token enrichi expiré — procéder au refresh
- Claim
organizationabsent du JWT Keycloak (Organizations Keycloak non configurées) keycloak_iden DB ne correspond pas ausubKeycloak
Action : Tenter un refresh token (une seule fois). Si le refresh échoue → redirect login.
Cas 2 — Token Bearer expiré ou invalide
Action : Refresh proactif ou redirect login.
Cas 3 — API Key invalide
json{ "statusCode": 401, "message": "Invalid API key", "error": "Unauthorized" }
Action : Vérifier la clé API (format sk_live_... ou sk_test_...).
402 — Payment Required
Crédits insuffisants pour une opération consommant des crédits.
json{ "statusCode": 402, "message": "Insufficient credits", "error": "Payment Required" }
Action : Rediriger vers la page d'achat de crédits (/billing/credits).
403 — Forbidden
L'utilisateur est authentifié mais n'a pas les droits nécessaires.
Cas 1 — Niveau admin insuffisant
json{ "statusCode": 403, "message": "Niveau d'administration insuffisant. Requis: CustomerAdmin, Actuel: User", "error": "Forbidden" }
Cas 2 — Droit applicatif manquant
json{ "statusCode": 403, "message": "Droit requis : users.create", "error": "Forbidden" }
Cas 3 — Feature désactivée
json{ "statusCode": 403, "message": "La fonctionnalité invitation n'est pas activée pour ce customer", "error": "Forbidden" }
Action : Afficher un message clair à l'utilisateur. Ne pas retenter automatiquement.
404 — Not Found
L'entité demandée n'existe pas ou n'est pas visible depuis le scope du token.
Anti-BOLA : un
CustomerAdminqui essaie d'accéder à un Customer qui ne lui appartient pas verra un404(pas un403). C'est intentionnel pour ne pas révéler l'existence de l'entité (OWASP BOLA pattern).
Action : Vérifier que l'identifiant (WID ou UUID) est correct et appartient au tenant accessible.
409 — Conflict
Une contrainte d'unicité est violée.
Cas courants :
- Email déjà utilisé dans le Customer
- Invitation
PENDINGdéjà existante pour cet email + Customer - Subdomain déjà utilisé par un autre Network
Action : Adapter l'UI pour informer l'utilisateur du conflit.
429 — Too Many Requests
Rate limit dépassé.
Headers : Retry-After: 2 (durée en secondes avant de pouvoir retenter)
Action : Respecter le Retry-After et implémenter un backoff exponentiel.
typescriptconst retryAfter = parseInt(res.headers.get("Retry-After") ?? "2", 10); await new Promise(r => setTimeout(r, retryAfter * 1000)); // 1 seul retry, puis échouer proprement
5xx — Erreurs serveur
Causes possibles :
- Service downstream injoignable (Keycloak, ws-serv-config, ws-serv-token)
- Erreur Prisma (contrainte DB inattendue)
- Timeout interne
- Problème de provisioning Keycloak (lors d'
/acceptinvitation)
Action : Retenter avec backoff exponentiel (max 3 tentatives). Si persistant, contacter le support avec le x-request-id.
typescript// Toujours logger le request-id pour le support const requestId = res.headers.get("x-request-id"); if (requestId) logger.error("API 500", { requestId, url, method });
Gestion d'erreurs recommandée côté frontend
typescriptclass ApiError extends Error { constructor( public readonly status: number, public readonly body: { statusCode: number; message: string | string[]; error: string } ) { super(Array.isArray(body.message) ? body.message.join(", ") : body.message); } get isForbidden() { return this.status === 403; } get isUnauthorized(){ return this.status === 401; } get isConflict() { return this.status === 409; } get isRateLimit() { return this.status === 429; } get isServerError() { return this.status >= 500; } }
Aller plus loin
- Erreurs métier : codes métier custom, messages exacts
- Pièges classiques : diagnostic des erreurs fréquentes
- Rate limits : limites par endpoint