PerpsError with a numeric code, the originating tool (provider), and a human-readable message.
PerpsError class, which carries the numeric error code:
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.
| 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 |
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 |
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 exportsPerpsErrorCode for type-safe error handling:
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
typedDatawas not modified after creation - The nonce has not expired (re-call
createActionto get fresh data)
AgentUnauthorized (2011)
The agent wallet is not approved on the provider. Check:- The setup flow completed successfully (via
checkSetupandexecuteProviderSetup) - 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
termsVersionbefore 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, notbtc) - 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
providerparameter 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)
- For Lighter, an account is created on first deposit
- The
providerparameter matches a provider where the user actually holds an account
InvalidNonce (2040)
The nonce in the request failed validation. Check:- The
typedDatawas 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
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
typedDatawas modified before signing - A different payload was substituted
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