WakaStart
Utilisation de l'API

Erreurs — Codes HTTP

Format d'erreur unifié, codes HTTP 400 à 5xx, causes et stratégies de gestion pour une Wakapp robuste.

Version v1.05 min de lecture

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 :

ChampTypeDescription
statusCodenumberCode HTTP
messagestring | string[]Description lisible du problème
errorstringLibellé 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 (teamLevel doit être NONE | 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 :

  1. Header x-enriched-token absent — vérifier le proxy serveur
  2. Cookie wakastart_token non posé (l'appel à /api/auth/enrich a échoué)
  3. Token enrichi expiré — procéder au refresh
  4. Claim organization absent du JWT Keycloak (Organizations Keycloak non configurées)
  5. keycloak_id en DB ne correspond pas au sub Keycloak

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 un 403 Forbidden, pas un 404. C'est ce que font les gardes de scope (assertTeamScope, assertNetworkScope, assertPartnerScope, assertAppFeatureScope dans config-api.service.ts de public-api) : rejet explicite via HttpStatus.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 PENDING dé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.

typescript
const 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'/accept invitation)

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

typescript
class 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