Skip to main content
All API errors return a PerpsError with a numeric code, the originating tool (provider), and a human-readable message.
{
  "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:
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.
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;
    }
  }
}
FieldTypeDescription
codePerpsErrorCodeNumeric error code — the value you branch on
messagestringHuman-readable message
toolstring?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+):
RangeCategoryDescription
2000-2009Base errorsServer errors, validation, timeout
2010-2019Auth errorsSignature and authorization failures
2020-2039Trading errorsOrder execution and market errors
2040-2049Nonce errorsNonce validation and expiry
2050-2059Payload errorsPayload integrity failures
2060-2069Routing errorsInvalid API routes

Error Code Reference

Base Errors (2000-2009)

CodeNameHTTPDescription
2000DefaultError500Unexpected server error
2001ServerError500Internal server error
2002ValidationError400Request validation failed
2003TimeoutError408Request timeout
2004ThirdPartyError424External service failure
2005SDKError500SDK-level error

Authentication Errors (2010-2019)

CodeNameHTTPDescription
2010SignatureInvalid401Signature verification failed
2011AgentUnauthorized403Agent wallet not approved
2012TermsNotAccepted401Terms of service not accepted for this address

Trading Errors (2020-2039)

CodeNameHTTPDescription
2020ExchangeRejected422Provider rejected the operation
2021InsufficientMargin422Not enough margin for the order
2022InsufficientBalance422Not enough balance
2023MarketNotFound404Unknown market/symbol
2024OrderNotFound404Order doesn’t exist
2025PositionNotFound404Position doesn’t exist
2026AccountNotFound404Account doesn’t exist on the provider

Nonce Errors (2040-2049)

CodeNameHTTPDescription
2040InvalidNonce400Nonce validation failed
2041NonceAlreadyUsed409Nonce already consumed
2042NonceExpired410Nonce TTL exceeded

Payload Errors (2050-2059)

CodeNameHTTPDescription
2050PayloadMismatch400Signed payload doesn’t match stored

Routing Errors (2060-2069)

CodeNameHTTPDescription
2060RouteNotFound404Invalid API route / endpoint does not exist

SDK Error Code Constants

The SDK exports PerpsErrorCode for type-safe error handling:
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

// 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 executeProviderSetup)
  • 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)
  • 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