Erreurs & internationalisation¶
Le flux d'erreur traverse trois éléments : les classes d'erreur de domaine (domain/errors.ts), les filtres d'exception HTTP (web/exception.http-filter.ts) qui les traduisent en réponses, et le service i18n (app/i18n/) qui localise les messages.
Règle d'or¶
Toujours lever une erreur de domaine
Les services lèvent des classes d'erreur de domaine — jamais throw new Error('...'), qui court-circuite le filtre HTTP et produit un 500.
Classes d'erreur de domaine¶
| Classe | Statut HTTP | Charge utile |
|---|---|---|
ValidationError |
400 |
{ [champ]: string[] } |
AuthenticationError |
401 |
{ message, code, workspaces? } |
AuthorizationError |
403 |
{ message } |
NotFoundError |
404 |
{ message } |
ConflictError |
409 |
{ message, code? } |
RateLimitError |
429 |
{ message } + en-tête Retry-After |
NotImplementedError |
501 |
{ message } |
PaymentGatewayError |
503 |
{ message } |
LedgerError |
500 |
format NestJS par défaut (voir ci-dessous) |
Les erreurs Zod (ZodError) sont également mappées en 400, sous le même format dictionnaire que ValidationError.
LedgerError est volontairement non filtrée
LedgerError protège un invariant de comptabilité en partie double construit côté service, pas une entrée client. Elle n'a aucun filtre @Catch : elle résout en 500 et reste visible pour le suivi d'erreurs (Sentry). Ne la repliez jamais dans ValidationError (400), ce qui ferait passer un bug comptable pour une erreur utilisateur et éteindrait l'alerte.
Le code machine de AuthenticationError¶
AuthenticationError porte un code machine-lisible que le frontend utilise pour brancher sa logique (rafraîchir le token, proposer un sélecteur de workspace, écran d'activation…) :
invalid_credentials | missing_authorization | workspace_header_required
| no_membership | account_not_activated | user_suspended
Pour workspace_header_required, l'erreur transporte aussi workspaceOptions → la liste workspaces renvoyée au client. Détail complet et tableau des actions frontend : Erreurs API.
Localisation (i18n)¶
Tous les messages sont localisés par le backend : le champ message (et les messages de validation) contient du texte prêt à afficher, traduit selon :
- la langue du compte (
user.language) si la requête est authentifiée ; - sinon l'en-tête
Accept-Language; - sinon la langue par défaut.
Les classes d'erreur transportent une clé de message (messageKey, ex. errors.common.not_found) et d'éventuels params, pas du texte figé. Le filtre HTTP résout la clé en texte localisé au moment de la réponse.
| Élément | Emplacement |
|---|---|
| Service i18n | app/i18n/i18n.service.ts |
| Locales (FR/EN) | app/i18n/locales/ |
| Mapping d'erreurs Zod | app/i18n/zod-error-map.ts (clés i18n pour la validation) |
| Résolution de locale | app/i18n/locale.ts |
Côté frontend
Le frontend ne re-traduit pas ces messages. Pour brancher un comportement, il s'appuie sur le status code et, quand il existe, le champ code — jamais sur le texte du message.
Filtres et interception globale¶
Les filtres d'exception sont enregistrés dans web/app.module.ts (APP_FILTER), aux côtés du filtre global Sentry et de l'intercepteur de contexte. Voir Erreurs API pour le contrat HTTP complet exposé au frontend.