WakaStart
Utilisation de l'API

Erreurs — Codes métier

Codes d'erreur métier Wakastart, messages exacts testables programmatiquement et mappings d'état.

Version v1.03 min de lecture

Erreurs — Codes métier

Ce chapitre liste les messages d'erreur avec valeur exacte et stable que vous pouvez tester programmatiquement. Lisez Erreurs HTTP pour les codes HTTP standard.


Messages stricts (testables programmatiquement)

Certains message ont une valeur exacte et stable :

MessageStatusSignification
"Token enrichi manquant ou invalide"401wakaToken absent ou invalide
"Insufficient credits"402Quota crédits dépassé
"La fonctionnalité invitation n'est pas activée pour ce customer"403Feature invitation désactivée
"Invalid API key"401Clé API inconnue ou révoquée
"Invitation non trouvée"200 (valid: false)Token d'invitation inconnu (via verify)
"Cette invitation a déjà été acceptée"200 (valid: false)Token déjà utilisé
"Cette invitation a expiré"200 (valid: false)expiresAt < now()
"Cette invitation a été annulée"200 (valid: false)Statut CANCELLED

Note : les messages verify retournent toujours 200 avec { valid: false, errorMessage: "..." } — pas un code d'erreur HTTP.


Erreurs d'invitation — traitement exhaustif

typescript
// Gérer les 4 états d'erreur d'une invitation async function loadInvitation(token: string) { const res = await fetch(`/api/proxy/invitations/verify/${token}`); const { valid, invitation, errorMessage } = await res.json(); if (!valid) { switch (errorMessage) { case "Cette invitation a expiré": return { error: "expired", ui: "Ce lien d'invitation a expiré. Demandez un nouveau lien." }; case "Cette invitation a déjà été acceptée": return { error: "accepted", ui: "Ce compte existe déjà. Connectez-vous directement." }; case "Cette invitation a été annulée": return { error: "cancelled", ui: "Cette invitation a été annulée par l'administrateur." }; case "Invitation non trouvée": default: return { error: "not_found", ui: "Ce lien d'invitation est invalide." }; } } return { valid: true, invitation }; }

Erreurs de droits — messages structurés

Les erreurs 403 liées aux droits contiennent des informations structurées dans message :

typescript
// Extraire le niveau requis vs niveau actuel d'un 403 function parseAdminLevelError(message: string) { const match = message.match(/Requis: (\w+), Actuel: (\w+)/); if (match) { return { required: match[1], actual: match[2] }; } return null; } // Usage const error = parseAdminLevelError( "Niveau d'administration insuffisant. Requis: CustomerAdmin, Actuel: User" ); // → { required: "CustomerAdmin", actual: "User" }

Codes de rate limit par endpoint

Certains endpoints ont des messages de rate limit spécifiques qui permettent d'identifier l'endpoint concerné :

typescript
// Pattern de détection rate limit function isRateLimitError(status: number, headers: Headers): boolean { return status === 429 && headers.has("Retry-After"); } function getRateLimitWait(headers: Headers): number { return parseInt(headers.get("Retry-After") ?? "2", 10) * 1000; }

Erreurs de validation — parsing

Pour les 400 avec tableau de messages, construire une map de champs en erreur :

typescript
interface ValidationErrors { [field: string]: string[]; } function parseValidationErrors( messages: string | string[] ): ValidationErrors { const msgs = Array.isArray(messages) ? messages : [messages]; const result: ValidationErrors = {}; for (const msg of msgs) { // Format NestJS : "fieldName must be ..." const firstSpace = msg.indexOf(" "); if (firstSpace > -1) { const field = msg.substring(0, firstSpace); const error = msg.substring(firstSpace + 1); result[field] = [...(result[field] ?? []), error]; } } return result; } // Usage avec react-hook-form try { await createUser(dto); } catch (err) { if (err instanceof ApiError && err.status === 400) { const fieldErrors = parseValidationErrors(err.body.message); Object.entries(fieldErrors).forEach(([field, errors]) => { form.setError(field as any, { message: errors[0] }); }); } }

Erreurs de conflit (409) — stratégies

typescript
try { await createInvitation(dto); toast.success("Invitation envoyée"); } catch (err) { if (err instanceof ApiError && err.isConflict) { const msg = err.body.message as string; if (msg.includes("invitation") && msg.includes("PENDING")) { // Invitation déjà existante — proposer de renvoyer setConflictAction("resend"); } else if (msg.includes("utilisateur")) { // Utilisateur déjà dans le Customer toast.error("Cet email est déjà utilisé dans ce Customer."); } } }

Aller plus loin