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": "Cle API invalide", "error": "Unauthorized" }
Le message exact varie selon la cause (clé absente, invalide ou expirée — voir Erreurs métier). Ne testez pas une valeur littérale de message : basez votre logique sur le code HTTP
401.
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'identifiant demandé (WID ou UUID) n'existe pas en base.
Violation de périmètre multi-tenant = 403, pas 404. Un
CustomerAdmin(ou tout token scopé) qui essaie d'accéder à une Team, un Network, un Partner ou une App hors de son périmètre reçoit un403 Forbidden, pas un404. C'est ce que font les gardes de scope (assertTeamScope,assertNetworkScope,assertPartnerScope,assertAppFeatureScopedansconfig-api.service.tsdepublic-api) : rejet explicite viaHttpStatus.FORBIDDEN. Il n'existe aucun pattern "404 anti-BOLA" côté API — voir 403 — Forbidden ci-dessus pour ce cas.
Action : Vérifier que l'identifiant (WID ou UUID) est correct.
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, le service de configuration WakaStart, le service d'authentification WakaStart)
- 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 l'horodatage, la route appelée et le corps de la réponse d'erreur (x-request-id n'est pas implémenté à ce jour).
typescript// Pas de x-request-id : logger route + méthode + statut + body pour le support logger.error("API 500", { url, method, status: res.status });
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