Skip to main content
All API errors return a PerpsError with a numeric code, the originating tool (provider), and a human-readable message.
In the SDK, every error is an instance of the single 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.
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+):

Error Code Reference

Base Errors (2000-2009)

Authentication Errors (2010-2019)

Trading Errors (2020-2039)

Nonce Errors (2040-2049)

Payload Errors (2050-2059)

Routing Errors (2060-2069)

Setup Errors (2070-2079)

Capability Errors (2080-2089)

Rate-Limit Errors (2090-2099)

SDK Error Code Constants

The SDK exports PerpsErrorCode for type-safe error handling:

Resolving a code from an HTTP status

The SDK exports errorCodeFromStatus to resolve the PerpsErrorCode a non-ok HTTP response carries. The provider boundaries (Hyperliquid, Lighter, Ondo) and the LI.FI transport layer all call it to build the code on a PerpsError:
  • A 429 status always resolves to RateLimitExceeded. No per-boundary mapping can override it.
  • Every other status resolves to a per-boundary statusCodes override when one is given (for example Hyperliquid maps 401 to Unauthorized and 403 to AgentUnauthorized on its /info reads), else it resolves to the fallback code passed in.

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
  • On Ondo, this is also the fallback for any venue error code the backend does not classify more specifically (the venue’s own error detail is included), and the specific mapping for too_many_twap_orders

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
  • On Lighter, this also covers the account reporting below maintenance or initial margin, or too little margin to create the order
  • On Ondo, this also covers the venue envelope reporting insufficient_margin

InsufficientBalance (2022)

Account lacks funds for the operation. Check:
  • Deposit sufficient funds to your account
  • Check balances with getAccount()
  • On Lighter, this also covers the venue reporting insufficient collateral, or insufficient asset balance to cover the order or its fee

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)
  • On Ondo, this also covers an HMAC signature the venue rejected as malformed or invalid — verify the request body was not re-serialized after signing, since the signature covers the exact byte string

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
  • On Hyperliquid, this also covers a /info read that the venue rejected with a 403

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

Unauthorized (2013)

The caller credential was rejected. Check:
  • The x-lifi-api-key value is a valid, currently-active API key
  • The API key has an integration mapping in the Partner Service
  • The x-lifi-integrator value, if sent, matches the identity your API key resolves to — remove it or correct it if it differs
  • On Hyperliquid, this also covers a /info read that the venue rejected with a 401
  • On Lighter, every Unauthorized from a read means the same thing: re-provision the auth token. The venue reports a rejected token and a revoked token through the same code, so the SDK cannot distinguish the two
  • On Ondo, this also covers the venue rejecting the client-held API key (not found, or missing the required scope) on an HMAC-signed trading action, and an HTTP 401 on any request — a 401 outranks any other body code the venue returns, so a rejected session still evicts the stored session credential. The SDK evicts a key or session the venue reports as unauthorized and creates a fresh one on the next trading action — re-run the failed 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
  • getQuote also throws this code when the market is a known market but the provider returned no live price for it

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
  • On Ondo, this also covers a twapId the venue no longer recognizes for cancelTwapOrder

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 or Ondo / Setup)
  • For Lighter, an account is created on first deposit
  • For Ondo, the user has completed the SIWE login — the session credential must be live
  • 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
  • On Lighter, this also covers the venue reporting an invalid nonce, or a batch transaction whose nonce does not increase

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
  • On Ondo, replaying a placeOrder request — the SDK generates a fresh clientOrderId per request, and the venue rejects a reused one; the venue reports this as a clientOrderID_collision
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.

SetupRequired (2070)

A recoverable provider setup action must be completed before the operation can be retried. Check:
  • Re-run checkSetup to get the current required setup action
  • Complete the returned setup action, then resubmit the original request
  • For Lighter integrator fee approval, this re-emits the approveIntegrator action for the current recipient and per-transaction-type rates
  • For Ondo, this covers a missing SIWE session, an unregistered API key, or unaccepted venue terms — re-run checkSetup to see which step is outstanding

FeatureUnavailable (2080)

The venue does not offer the requested capability. Check:
  • The feature is not available on this provider — no request change or setup step makes it succeed
  • Consult the provider’s page for the capabilities it supports before offering the entry point
  • Ondo has no withdraw, sendAsset, modifyOrder, or updatePositionMargin methods and no account options; the venue also disables specific features at times, surfaced through this same code

RateLimitExceeded (2090)

The caller spent its request budget for the current one-minute window. Check:
  • Wait for the current window to reset, then retry
  • An API key with a negotiated rate limit gets a higher budget than the default. See Rate Limits
  • A 429 from the venue itself also raises this code — no per-boundary mapping can override it. This covers a Hyperliquid /info read and an Ondo request that returned 429

ServerError (2001)

No answer arrived: the request itself failed before any response, for example the fetch call threw (network failure, DNS failure, connection reset). Check:
  • Network connectivity between the SDK and the LI.FI API or the provider
  • Retry after a short delay — an idempotent read is safe to retry, a mutation is not, since the request may or may not have reached the venue

ThirdPartyError (2004)

The venue answered with a failure that no other code describes, so the outcome of the request stays open — the venue received the request and rejected it for a reason the SDK does not classify more specifically. Check:
  • Provider may be experiencing issues
  • Check provider status pages for outages
  • Retry an idempotent read after a short delay. Do not blindly retry a mutation: since the venue answered, a blind retry risks a duplicate action if the original request actually succeeded venue-side

TimeoutError (2003)

The request exceeded the timeout limit. Check:
  • Network connectivity
  • Provider may be experiencing high latency
  • Retry the operation