Error codes
Every error response from the Management API and the hosted flows has the same shape:
{
"status": 400,
"message": "Invalid phone number. Include the country code, for example +34600123456.",
"error_code": "invalid_phone",
"docs": "https://faable.com/docs/auth/errors#invalid_phone",
"details": { "fields": ["phone"] }
}error_codeis the field to branch on. It is stable: a code is never renamed or reused with another meaning, and each code maps to exactly one HTTP status.messageis written for a person and may change between releases. Show it in a log; do not compare it, and do not show it to end users as it is — map the code to your own copy instead.detailsis optional and structured.validation_errorandalready_existscarryfields;validation_erroralso carriesissues(one{ path, message }per failing field).docslinks to the entry for the code on this page.
A few codes carry a detail after a colon — sms_unavailable:plan, sms_unavailable:quota. Branch on the prefix.
The one exception is the OAuth token endpoint (POST /oauth/token), which answers in the RFC 6749 shape — { "error", "error_description" } — and carries error_code as an extension member when the canonical error is too coarse (for example invalid_grant with error_code: "user_suspended").
With the SDK
@faable/auth-sdk throws a FaableApiError on any non-2xx. The code is on the error, and every generated method lists the codes it can throw in its docstring:
import { FaableApiError, FaableAuthApi } from '@faable/auth-sdk'
try {
await auth.userCreate({ name, email, phone })
} catch (e) {
if (e instanceof FaableApiError) {
switch (e.error_code) {
case 'already_exists':
return 'That email is already registered.'
case 'invalid_phone':
return 'Write the phone with its country code, e.g. +34 600 123 456.'
case 'validation_error':
return `Check ${e.details?.fields?.join(', ')}.`
}
if (e.isErrorCode('sms_unavailable'))
return 'SMS is not available right now.'
}
throw e
}ErrorCode is exported as a TypeScript union, so a switch over it is checked by the compiler.
The OpenAPI document
Every operation in /docs/json documents its error responses: one entry per status, with error_code restricted to the codes that operation can emit. Code generators pick them up from there.
Generic codes
When an error has no more specific code, error_code is derived from the status. You will always get one of these at minimum; the specific codes below are what you should reach for first.
| Code | Status |
|---|---|
bad_request | 400 |
validation_error | 400 |
unauthorized | 401 |
payment_required | 402 |
forbidden | 403 |
not_found | 404 |
method_not_allowed | 405 |
conflict | 409 |
payload_too_large | 413 |
unsupported_media_type | 415 |
unprocessable_entity | 422 |
too_many_requests | 429 |
internal_error | 500 |
All codes
Generated from the server’s catalogue (npm run dump-error-codes in the auth service); the list is the same one the OpenAPI ErrorCode schema enumerates.
Last updated on