# Failures

Jadpo raises the sign-in failures itself. You never declare them or list
them in an action's `fails`
([Declaring a failure](declaring-a-failure.md)). A client gets
them in the usual failure body, with the code and status below.

| Condition | Code | Kind | HTTP |
|---|---|---|---|
| No credential | `authentication_required` | `unauthenticated` | 401 |
| Malformed, expired, tampered or wrong-audience credential | `invalid_credentials` | `unauthenticated` | 401 |
| Two credentials, or a cookie and a bearer together | `ambiguous_credentials` | `unauthenticated` | 401 |
| Cookie write without the right `Origin` or CSRF header | `csrf_rejected` | `not_permitted` | 403 |
| The caller's record is not active | `user_disabled` / `service_disabled` | `not_permitted` | 403 |
| A service calls a route that selects `current_principal.user` | `not_permitted` | `not_permitted` | 403 |
| The database or OIDC provider cannot be reached | `authentication_unavailable` | `unavailable` | 503 |
| Keys or issuer missing or invalid | `authentication_misconfigured` | `misconfigured` | 500 |
| A magic link expired, used, replaced or unknown | `sign_in_link_expired` | `unauthenticated` | 401 |
| A magic link opened in another browser with `same_browser: true` | `sign_in_link_other_browser` | `unauthenticated` | 401 |
| A code wrong, used, ended, expired or replaced, or no code for the address | `sign_in_code_invalid` | `unauthenticated` | 401 |
| An address reached `failed_codes_per_day:` | `sign_in_codes_disabled` | `rate_limited` | 429 |
| `on_first_sign_in` refused, or no user and no `on_first_sign_in:` for a provider or token | `sign_in_refused` | `not_permitted` | 403 |
| Too many sends to one address | `rate_limited` | `rate_limited` | 429 |
| `magic_links.request` given a path to go back to that isn't on the app's site | `sign_in_return_invalid` | `invalid_value` | 422 |
| A passkey ceremony's challenge expired, was used, was replaced or came from another browser or session | `passkey_challenge_expired` | `unauthenticated` | 401 |
| A passkey's answer doesn't check (origin, relying party, presence, verification, signature or counter), or its passkey isn't registered | `passkey_rejected` | `unauthenticated` | 401 |
| The passkey is registered already | `passkey_already_registered` | `conflict` | 409 |
| The person has 20 passkeys already | `passkey_limit_reached` | `conflict` | 409 |
| No passkey of the signed-in person has that `id` | `passkey_not_found` | `not_found` | 404 |

`user_disabled` and `service_disabled` are built in and never declared,
as are the `sign_in_` and `passkey_` failures. Their wording for people is
Jadpo's own ([Built-in services](built-in-services.md)).

With a [revocation delay](the-application-and-revocation.md#fewer-database-reads-revocation_delay), a
disabled caller is noticed within that delay. The log records the way in,
the credential's record id and the reason, never the credential.
