---
title: Error Codes
description: >-
  Learn the LayerX API's standardized JSON error structure, complete list of
  error codes, HTTP statuses, and guidance for retryable versus non-retryable
---

> **For AI agents:** the complete documentation index is at [llms.txt](/llms.txt). Append `.md` to any page URL for its markdown version.

## Error codes

All LayerX API errors follow a consistent JSON structure:

```json
{
  "error": {
    "code": "insufficient_funds",
    "message": "Transfer amount exceeds available balance.",
    "details": {
      "available": "10.00",
      "requested": "25.00"
    }
  }
}
```

The `code` field is a stable machine-readable string. The `message` field is a human-readable explanation. The `details` field is optional and contains context-specific information.

## Error codes

### `invalid_request`

The request body or parameters are malformed. This includes missing required fields, invalid DID formats, non-numeric amounts, or malformed JSON.

**HTTP status:** 400

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Field 'amount' must be a positive decimal string.",
    "details": { "field": "amount", "value": "-5" }
  }
}
```

### `unauthorized`

The request lacks a valid principal token, the token has expired, or the DID signature verification failed.

**HTTP status:** 401

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Principal token has expired."
  }
}
```

Re-run the [DID challenge/verify flow](/layerx/did-auth) to obtain a new token.

### `not_found`

The requested resource does not exist. This applies to receipt lookups, transfer queries, and DID lookups.

**HTTP status:** 404

```json
{
  "error": {
    "code": "not_found",
    "message": "No receipt found for sequence number 99999."
  }
}
```

### `insufficient_funds`

The sender's USDX balance is less than the requested transfer amount.

**HTTP status:** 402

```json
{
  "error": {
    "code": "insufficient_funds",
    "message": "Transfer amount exceeds available balance.",
    "details": { "available": "10.00", "requested": "25.00" }
  }
}
```

When returned in the context of a 402LXP flow, this indicates the agent cannot afford the service's price.

### `conflict`

The request conflicts with current state. Common causes:

- **Duplicate transfer** - a transfer with the same nonce has already been processed.
- **Already settled** - the requested transfer has already been force-settled.
- **Concurrent modification** - the account was modified by another request.

**HTTP status:** 409

```json
{
  "error": {
    "code": "conflict",
    "message": "Transfer with nonce 42 already exists."
  }
}
```

### `internal`

An unexpected server error occurred. These are logged and investigated automatically. Retry with exponential backoff.

**HTTP status:** 500

```json
{
  "error": {
    "code": "internal",
    "message": "An unexpected error occurred. Please retry."
  }
}
```

### `rate_limited`

The client has exceeded the rate limit. The `details` object includes the retry-after window.

**HTTP status:** 429

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded.",
    "details": { "retry_after_seconds": 5 }
  }
}
```

## Handling errors

### Retryable errors

- `internal` - retry with exponential backoff (1s, 2s, 4s, max 30s).
- `rate_limited` - wait for `retry_after_seconds` before retrying.
- `conflict` (duplicate nonce) - check if the transfer already succeeded before retrying.

### Non-retryable errors

- `invalid_request` - fix the request body before retrying.
- `unauthorized` - re-authenticate before retrying.
- `not_found` - the resource does not exist.
- `insufficient_funds` - deposit more USDX before retrying.
