They are stable, localized, and intentionally explicit so the frontend can react without guessing or parsing messages. Every error response is produced either by:
- global exception handling (
bootstrap/app.php) - domain controllers (auth, 2FA, registration, etc.)
Error contract (guaranteed)
Every error response follows one of the formats below.Standard error shape
codeStable, machine-readable identifier. Never localized. Never changes.messageHuman-readable explanation, always localized.- HTTP status Meaningful and intentional (see mapping below).
Validation error (HTTP 422)
Used exclusively for request validation failures.errorsis a field → array of messages map- messages are localized
- field names are never localized
- structure is stable across all endpoints
Localization of errors
Errors use the same locale resolution middleware as the rest of the API. Resolution order:X-App-Localeheader (recommended)Accept-Languageuser.locale(authenticated requests)- fallback:
fr
message- validation messages inside
errors - emails triggered during the request
code values are never localized and must always be used for logic.
HTTP status semantics (contract)
| HTTP status | Meaning | Frontend responsibility |
|---|---|---|
| 422 | Invalid request payload | Show inline field errors |
| 401 | Unauthenticated / invalid session | Clear token, redirect to login |
| 403 | Forbidden / invalid verification | Stay in flow, show error |
| 404 | Resource not found | Show not-found or restart flow |
| 409 | Conflict (state or business rule) | Propose alternative path |
| 410 | Gone (expired challenge / token) | Restart the related flow |
| 429 | Rate limited | Disable action, show cooldown |
| 500 | Server error (production-safe) | Show generic retry UI |
Global errors (from exception handling)
These errors can occur on any endpoint.Validation failed
HTTP:422
Code: VALIDATION_FAILED
- Render field-level errors next to inputs
- Do not show multiple global toasts
- Do not retry automatically
Unauthenticated
HTTP:401
Code: UNAUTHENTICATED
- Clear stored token
- Reset user state
- Redirect to login or onboarding entry point
Rate limited
HTTP:429
Code: RATE_LIMITED
- Disable the triggering action
- Show cooldown or timer
- Never retry automatically in a loop
Server error (production)
HTTP:500
Code: SERVER_ERROR
- Show generic error UI
- Offer a retry action
- Log client-side context if available
Authentication errors (anti-enumeration)
Authentication endpoints are deliberately strict.Invalid credentials
Returned for all of the following cases:- unknown email
- wrong password
- account not in
activestate
401
Code: INVALID_CREDENTIALS
- Never indicate which field failed
- Never suggest account existence
- Optional UX: show “Forgot password?” only if implemented
MFA (2FA) related errors
These errors occur during login or 2FA management.| Code | HTTP | Meaning |
|---|---|---|
MFA_REQUIRED | 200 | Second factor required (challenge issued) |
MFA_CHALLENGE_GONE | 410 | Challenge expired or missing |
MFA_CHALLENGE_INVALID | 400 | Challenge binding mismatch (IP / UA / device) |
MFA_CODE_INVALID | 403 | Wrong TOTP code |
MFA_TOO_MANY_ATTEMPTS | 429 | Too many attempts on the same challenge |
- Restart login on
MFA_CHALLENGE_GONE - Do not retry automatically
- Lock UI on
MFA_TOO_MANY_ATTEMPTS
Registration & onboarding errors
| Code | HTTP | Meaning |
|---|---|---|
EMAIL_ALREADY_USED | 409 | Email already belongs to active account |
EMAIL_ALREADY_ACTIVE | 409 | Account already activated |
USER_NOT_FOUND | 404 | No user for resend flow |
OTP_INVALID | 422/403 | Invalid or expired email code |
MAGIC_LINK_INVALID | 403 | Expired or invalid magic link |
- Always propose the next valid step
- Never reveal internal state
- Restart flows cleanly when needed
Frontend best practices (mandatory)
- Always branch logic on
code, never onmessage - Treat
429as a product constraint, not a bug - Never expose backend internals to users
- Never infer account existence from errors
- Assume all messages are localized
Guarantees
Flowxi error handling guarantees:- stable and documented error codes
- deterministic HTTP statuses
- strict anti-enumeration
- full localization coverage
- production-safe responses only
Testing checklist
- Verify identical responses for wrong email vs wrong password
- Trigger validation errors and inspect
errorsstructure - Hit rate limits and ensure UI cooldown
- Test expired MFA challenges
- Switch
X-App-Localeand verify translated messages

