---
title: Error codes
---

# Error codes

Every error response follows the OAuth 2.0 / OIDC convention:

```json
{
  "error": "invalid_request",
  "error_description": "Human-readable explanation",
  "error_uri": "https://developers.littlexlittle.org/reference/errors/#invalid_request"
}
```

For redirect-based errors, the same fields appear as query parameters on the `redirect_uri`.

## Standard OAuth 2.0 errors

| Code | HTTP | When |
|---|---|---|
| `invalid_request` | 400 | Missing or malformed parameter. |
| `invalid_client` | 401 | `client_id` unknown, suspended, or wrong secret. |
| `invalid_grant` | 400 | Authorization code expired, used twice, or refresh token rotated/revoked. |
| `unauthorized_client` | 400 | Client is not allowed to use the requested grant type. |
| `unsupported_grant_type` | 400 | `grant_type` value isn't recognized. |
| `invalid_scope` | 400 | Requested scope is unknown or not allowed for this client. |
| `access_denied` | 302 redirect | User declined consent. |
| `server_error` | 500 | Internal failure. Try again later; alert support if persistent. |
| `temporarily_unavailable` | 503 | Upstream maintenance window. Retry with backoff. |

## OIDC-specific errors

| Code | When |
|---|---|
| `interaction_required` | `prompt=none` was used but the user must interact (consent/login). |
| `login_required` | `prompt=none` was used but no upstream session exists. |
| `account_selection_required` | `prompt=none` was used but multiple accounts match. |
| `consent_required` | `prompt=none` was used but consent is not yet granted. |
| `invalid_request_uri` | `request_uri` is not allowed. |
| `invalid_request_object` | The signed request object failed verification. |
| `request_not_supported` | Server doesn't support `request` parameter. |
| `request_uri_not_supported` | Server doesn't support `request_uri` parameter. |
| `registration_not_supported` | Self-serve registration is closed (current state). |

## Little X Little custom errors

| Code | When |
|---|---|
| `lxl_tenant_mismatch` | Account belongs to a different NGO than the client. |
| `lxl_app_suspended` | The NGO app for this client is currently suspended. |
| `lxl_user_suspended` | The user account is suspended. |
| `lxl_rate_limited` | Too many requests. See `Retry-After` header. |
| `lxl_pkce_required` | Public client did not include `code_challenge`. |

## Handling tips

- **Do not retry on `invalid_grant`.** The user must re-authenticate.
- **Always retry on `temporarily_unavailable`** with exponential backoff (start 1s, max 5m, jitter ±20%).
- **Log `error_uri`** — it links to this page anchored at the specific code.
- **Show `error_description` to developers, not end users.** Translate to friendly copy in your UI.
