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

# Error Codes

> Error codes, HTTP status codes, and troubleshooting guide

All API errors return a `PerpsError` with a numeric `code`, the originating `tool` (provider), and a human-readable `message`.

```json theme={null}
{
  "code": 2021,
  "tool": "hyperliquid",
  "message": "Insufficient margin for order"
}
```

In the SDK, every error is an instance of the single `PerpsError` class, which carries the numeric error code:

```typescript theme={null}
import { PerpsError, PerpsErrorCode } from '@lifi/perps-sdk';

try {
  await perps.placeOrder({ ... });
} catch (error) {
  if (error instanceof PerpsError) {
    console.log(error.code);    // 2021
    console.log(error.message); // 'Insufficient margin for order'

    // Use named constants for readable error handling
    if (error.code === PerpsErrorCode.InsufficientMargin) {
      // Handle insufficient margin
    }
  }
}
```

### Discriminating errors

The error model is **flat**: there is one class — `PerpsError` — and you branch on its numeric `.code` (a `PerpsErrorCode`). There are no error subclasses to `instanceof`. A non-2xx API response is re-hydrated into a `PerpsError` carrying the backend's `code` and `message`; `error.tool` names the originating provider when the error came from one.

```typescript theme={null}
import { PerpsError, PerpsErrorCode } from '@lifi/perps-sdk';

try {
  await perps.placeOrder({ ... });
} catch (error) {
  if (error instanceof PerpsError) {
    switch (error.code) {
      case PerpsErrorCode.InsufficientMargin:
        // not enough margin for the order
        break;
      case PerpsErrorCode.AgentUnauthorized:
        // re-run the setup flow
        break;
      case PerpsErrorCode.ThirdPartyError:
        console.log(error.tool); // the DEX that produced the error
        break;
    }
  }
}
```

| Field     | Type             | Description                                            |
| --------- | ---------------- | ------------------------------------------------------ |
| `code`    | `PerpsErrorCode` | Numeric error code — the value you branch on           |
| `message` | `string`         | Human-readable message                                 |
| `tool`    | `string?`        | Originating provider/DEX, when the error came from one |

SDK-originated errors (for example a missing agent key) carry canonical messages from the `PerpsErrorMessage` enum.

## Error Code Ranges

Error codes use the **2000+ range** to avoid collision with the main LI.FI API (which uses 1000+):

| Range     | Category       | Description                          |
| --------- | -------------- | ------------------------------------ |
| 2000-2009 | Base errors    | Server errors, validation, timeout   |
| 2010-2019 | Auth errors    | Signature and authorization failures |
| 2020-2039 | Trading errors | Order execution and market errors    |
| 2040-2049 | Nonce errors   | Nonce validation and expiry          |
| 2050-2059 | Payload errors | Payload integrity failures           |
| 2060-2069 | Routing errors | Invalid API routes                   |

## Error Code Reference

### Base Errors (2000-2009)

| Code | Name              | HTTP | Description               |
| ---- | ----------------- | ---- | ------------------------- |
| 2000 | `DefaultError`    | 500  | Unexpected server error   |
| 2001 | `ServerError`     | 500  | Internal server error     |
| 2002 | `ValidationError` | 400  | Request validation failed |
| 2003 | `TimeoutError`    | 408  | Request timeout           |
| 2004 | `ThirdPartyError` | 424  | External service failure  |
| 2005 | `SDKError`        | 500  | SDK-level error           |

### Authentication Errors (2010-2019)

| Code | Name                | HTTP | Description                                    |
| ---- | ------------------- | ---- | ---------------------------------------------- |
| 2010 | `SignatureInvalid`  | 401  | Signature verification failed                  |
| 2011 | `AgentUnauthorized` | 403  | Agent wallet not approved                      |
| 2012 | `TermsNotAccepted`  | 401  | Terms of service not accepted for this address |
| 2013 | `Unauthorized`      | 401  | Rejected or invalid caller credential          |

### Trading Errors (2020-2039)

| Code | Name                  | HTTP | Description                           |
| ---- | --------------------- | ---- | ------------------------------------- |
| 2020 | `ExchangeRejected`    | 422  | Provider rejected the operation       |
| 2021 | `InsufficientMargin`  | 422  | Not enough margin for the order       |
| 2022 | `InsufficientBalance` | 422  | Not enough balance                    |
| 2023 | `MarketNotFound`      | 404  | Unknown market/symbol                 |
| 2024 | `OrderNotFound`       | 404  | Order doesn't exist                   |
| 2025 | `PositionNotFound`    | 404  | Position doesn't exist                |
| 2026 | `AccountNotFound`     | 404  | Account doesn't exist on the provider |

### Nonce Errors (2040-2049)

| Code | Name               | HTTP | Description             |
| ---- | ------------------ | ---- | ----------------------- |
| 2040 | `InvalidNonce`     | 400  | Nonce validation failed |
| 2041 | `NonceAlreadyUsed` | 409  | Nonce already consumed  |
| 2042 | `NonceExpired`     | 410  | Nonce TTL exceeded      |

### Payload Errors (2050-2059)

| Code | Name              | HTTP | Description                         |
| ---- | ----------------- | ---- | ----------------------------------- |
| 2050 | `PayloadMismatch` | 400  | Signed payload doesn't match stored |

### Routing Errors (2060-2069)

| Code | Name            | HTTP | Description                                 |
| ---- | --------------- | ---- | ------------------------------------------- |
| 2060 | `RouteNotFound` | 404  | Invalid API route / endpoint does not exist |

## SDK Error Code Constants

The SDK exports `PerpsErrorCode` for type-safe error handling:

```typescript theme={null}
import { PerpsErrorCode } from '@lifi/perps-sdk';

// Base errors
PerpsErrorCode.DefaultError      // 2000
PerpsErrorCode.ServerError       // 2001
PerpsErrorCode.ValidationError   // 2002
PerpsErrorCode.TimeoutError      // 2003
PerpsErrorCode.ThirdPartyError   // 2004
PerpsErrorCode.SDKError          // 2005

// Auth errors
PerpsErrorCode.SignatureInvalid  // 2010
PerpsErrorCode.AgentUnauthorized // 2011
PerpsErrorCode.TermsNotAccepted  // 2012
PerpsErrorCode.Unauthorized      // 2013

// Trading errors
PerpsErrorCode.ExchangeRejected    // 2020
PerpsErrorCode.InsufficientMargin  // 2021
PerpsErrorCode.InsufficientBalance // 2022
PerpsErrorCode.MarketNotFound      // 2023
PerpsErrorCode.OrderNotFound       // 2024
PerpsErrorCode.PositionNotFound    // 2025
PerpsErrorCode.AccountNotFound     // 2026

// Nonce errors
PerpsErrorCode.InvalidNonce      // 2040
PerpsErrorCode.NonceAlreadyUsed  // 2041
PerpsErrorCode.NonceExpired      // 2042

// Payload errors
PerpsErrorCode.PayloadMismatch   // 2050

// Routing errors
PerpsErrorCode.RouteNotFound     // 2060
```

## Troubleshooting

### ExchangeRejected (2020)

The provider backend rejected the request. Check:

* Order parameters are valid (size within limits, price within bounds)
* The market is active and not in maintenance
* Request body matches the expected schema

### InsufficientMargin (2021)

Not enough margin for the requested order. Check:

* Account has sufficient USDC balance
* Existing positions don't consume too much margin
* Reduce leverage or order size

### InsufficientBalance (2022)

Account lacks funds for the operation. Check:

* Deposit sufficient funds to your account
* Check balances with `getAccount()`

### SignatureInvalid (2010)

The signature could not be verified. Check:

* The correct wallet/agent signed the `typedData`
* The `typedData` was not modified after creation
* The nonce has not expired (re-call `createAction` to get fresh data)

### AgentUnauthorized (2011)

The agent wallet is not approved on the provider. Check:

* The setup flow completed successfully (via `checkSetup` and `executeProviderSetupAction`)
* The agent address matches what was authorized
* The authorization has not been revoked

### TermsNotAccepted (2012)

The address has not accepted the current terms of service. Check:

* Fetch the current terms and acceptance status with `GET /v1/perps/meta/terms?address=`
* Have the address accept the current `termsVersion` before performing an action

### MarketNotFound (2023)

The symbol doesn't exist on the specified provider. Check:

* Use `getAssets()` to list valid asset IDs
* Asset IDs are case-sensitive (use `BTC`, not `btc`)
* The asset hasn't been delisted

### OrderNotFound (2024)

The order ID doesn't exist. Check:

* Use the correct `orderId` (from the submit response)
* The order may have already been fully filled or cancelled
* Verify the `provider` parameter matches where the order was placed

### PositionNotFound (2025)

No position exists for the specified symbol. Check:

* Verify you have an open position for this symbol
* Use `getPositions()` to list current positions

### AccountNotFound (2026)

The address has not been onboarded on the specified provider. Check:

* The user has completed the setup flow for the provider (see [Hyperliquid / Setup](/providers/hyperliquid/setup))
* For Lighter, an account is created on first deposit
* The `provider` parameter matches a provider where the user actually holds an account

### InvalidNonce (2040)

The nonce in the request failed validation. Check:

* The `typedData` was not modified after creation
* Re-call the create endpoint to get fresh payloads

### NonceAlreadyUsed (2041)

The nonce has already been consumed by a previous request. This typically happens when:

* Submitting the same signed payload twice
* Re-submitting after a successful operation

Solution: Call the create endpoint again to get new payloads with fresh nonces.

### NonceExpired (2042)

The nonce has exceeded its time-to-live. Payloads expire after a short period for security. Check:

* Sign and submit payloads promptly after creation
* Re-call the create endpoint to get fresh payloads

### PayloadMismatch (2050)

The signed payload doesn't match what was stored server-side. This can happen if:

* The `typedData` was modified before signing
* A different payload was substituted

Solution: Use the exact `typedData` returned from the create endpoint without modification.

### ThirdPartyError (2004)

An external service (typically the provider) returned an error. Check:

* Provider may be experiencing issues
* Retry after a short delay
* Check provider status pages for outages

### TimeoutError (2003)

The request exceeded the timeout limit. Check:

* Network connectivity
* Provider may be experiencing high latency
* Retry the operation
