> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useroutr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> One error shape, a stable code for every failure, and what each one asks you to do.

Every failure uses the same envelope, and `code` never changes meaning. Branch on `code`, never
on `message`.

```json theme={null}
{
  "error": {
    "code": "INVALID_DESTINATION",
    "message": "The destination asset must match the required asset.",
    "requestId": "req_5f0c8a21",
    "retryable": false,
    "details": [{ "path": "/amount", "message": "must be a decimal string" }]
  }
}
```

* `requestId` matches the `X-Request-ID` header. Quote it when you contact support, or find the
  call in your [request logs](/guides/request-logs).
* `retryable` is `true` when the same request can succeed later.
* `details` lists each invalid field on a validation error.

Stack traces, database and chain errors are never returned.

## In the SDK

Every failure is a `UseroutrError`, and a subclass tells you the kind:

| Class | When |
| - | - |
| `ValidationError` | `400`: the request is invalid; see `details` |
| `AuthenticationError` | `401`: the key is missing, wrong, revoked or for the other environment |
| `AuthorizationError` | `403`: for example, the application is suspended |
| `NotFoundError` | `404`: not found, including anything owned by another application |
| `RateLimitError` | `429`: slow down; the SDK already waited and retried |
| `ApiError` | Any other API error: `409`, `422`, `5xx` |
| `ConnectionError` | The request never completed. `code` is `TIMEOUT` or `CONNECTION_ERROR`. |
| `WebhookSignatureError` | A webhook failed verification. Not a `UseroutrError`. |

```ts theme={null}
import { ApiError, UseroutrError } from "@useroutr/sdk";

try {
  await useroutr.quotes.select(quoteId);
} catch (error) {
  if (error instanceof ApiError && error.code === "QUOTE_EXPIRED") {
    // quote again
  } else if (error instanceof UseroutrError && error.retryable) {
    // try again later
  } else {
    throw error;
  }
}
```

## Codes

### Your request

| Code | HTTP | What to do |
| - | - | - |
| `VALIDATION_ERROR` | 400 | Fix the fields listed in `details` |
| `BAD_REQUEST` | 400, 413, 415 | The request is malformed, too large or of the wrong content type |
| `IDEMPOTENCY_KEY_REQUIRED` | 400 | Send an `Idempotency-Key` header |
| `IDEMPOTENCY_KEY_REUSED` | 409 | The key was used with a different body. Use a new key. |
| `IDEMPOTENCY_REQUEST_IN_PROGRESS` | 409 | The first request with this key is still running. Retry with the same key. |
| `CONFLICT` | 409 | The resource is in a state that conflicts, for example another quote is locked |
| `NOT_FOUND` | 404 | No such resource for your application |

### Access

| Code | HTTP | What to do |
| - | - | - |
| `UNAUTHORIZED` | 401 | Check the API key |
| `INVALID_CREDENTIALS` | 401 | The key is for the other environment: a test key on production or a live key on the sandbox |
| `FORBIDDEN` | 403 | The key cannot do this |
| `APPLICATION_INACTIVE` | 403 | The application is suspended or archived |
| `RATE_LIMITED` | 429 | Wait for `Retry-After`, then retry |

### Funding intents and quotes

| Code | HTTP | What to do |
| - | - | - |
| `INVALID_AMOUNT` | 422 | Use a positive decimal within the asset's precision |
| `UNSUPPORTED_ASSET` | 422 | Use an asset id from `assets.list()` |
| `INVALID_DESTINATION` | 422 | The destination cannot receive this asset; `message` says why |
| `INVALID_EXPIRATION` | 422 | Use an `expires_at` in the future and at most 24 hours ahead |
| `NO_ROUTE_AVAILABLE` | 422 | No route delivers this pair. Offer another asset. |
| `INSUFFICIENT_INPUT` | 422 | An exact-input amount buys less than you asked for |
| `QUOTE_EXPIRED` | 422 | Create a new quote |
| `QUOTE_NOT_SELECTABLE` | 422 | The intent cannot take a quote now |
| `QUOTE_NOT_LOCKED` | 422 | The quote is not the intent's locked quote |
| `PROTOCOL_REJECTED` | 422 | The protocol refused the intent |
| `PROTOCOL_UNAVAILABLE` | 503 | Retryable. The protocol did not answer in time. |
| `ROUTE_UNAVAILABLE` | 503 | Retryable. No provider answered in time. |

### Branding

| Code | HTTP | What to do |
| - | - | - |
| `LOGO_FORMAT_UNSUPPORTED` | 422 | Upload a PNG, JPEG or WebP |
| `LOGO_DIMENSIONS_INVALID` | 422 | Use an image 64 to 1024 pixels on each side |
| `LOGO_STORAGE_UNAVAILABLE` | 503 | Retryable. Logo storage is not available. |

### Anything else

| Code | HTTP | What to do |
| - | - | - |
| `INTERNAL_ERROR` | 500 | Something failed on our side. Before repeating a write without an idempotency key, check whether it took effect. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.