Skip to main content
API errors return a JSON body { code, tool?, message }: a numeric code, the originating tool (provider) when one produced the error, and a human-readable message. The SDK throws it as a PerpsError.
Every error the SDK raises through its HTTP and venue layers is an instance of the single PerpsError class, which carries the numeric error code. Wallet errors, AbortError, and WebSocket-channel errors are not PerpsError; detect a user rejection through EIP-1193 code 4001 or UserRejectedRequestError along the cause chain.

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, and is the string "unknown" for backend errors that carry no provider.
SDK-originated errors (for example a missing agent key) carry canonical messages from the PerpsErrorMessage enum. A missing or corrupt local agent key is SDKError (2005) with PerpsErrorMessage.AgentNotFound or AgentStorageCorrupt; compare error.message. A venue refusal inside a 202 action response does not throw for the execution methods. The refusal appears as a result with status Failed, with error and, when known, errorCode. An unclear venue answer appears as Indeterminate. The HTTP status, the action status and the errorCode are three separate axes. Check each result as well as catching thrown errors.

Account access errors

A data read such as getPositions or getOrders throws the account’s access state as one of three codes: SetupRequired (no credential is stored), Unauthorized (the venue rejected the credential), or AccountNotFound (the wallet has no venue account). A status read such as checkSetup returns the state as a value. getAccount is provider-specific: Hyperliquid treats it as a data read, while Ondo and Lighter expose status behavior subject to Lighter’s documented exceptions. See Account access for the rule, the per-provider table, and what to render for each code.

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 Hyperliquid and Ondo provider boundaries and the LI.FI transport layer 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.
Lighter does not use errorCodeFromStatus. It signals rate limits with 429 or 405, and the SDK holds all Lighter requests until Retry-After expires (capped at 5 minutes, 60 seconds when the header is missing or invalid), throwing RateLimitExceeded meanwhile. Other Lighter body codes map through the SDK’s own table and default to ThirdPartyError.

Retryability

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 an SDK-side read reporting insufficient_margin. A write relayed through the backend that reports it returns ExchangeRejected (2020)

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)

A Hyperliquid /info read was rejected with a 403. Check:
  • The address and agent match an account the venue recognises
  • Re-run the setup flow (via checkSetup and executeProviderSetupAction) if the agent was never approved
A missing local agent key is not this code: it throws SDKError (2005) with PerpsErrorMessage.AgentNotFound or AgentStorageCorrupt. A venue rejection of an unapproved agent at order submission surfaces as ExchangeRejected (2020).

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 is linked to an integration in the LI.FI Partner Portal
  • On Hyperliquid, this also covers a /info read that the venue rejected with a 401
  • On Lighter and Ondo, every Unauthorized from a read means the venue refused the credential you presented. SetupRequired means that no credential was stored. Lighter reports a rejected token and a revoked token through the same code, so the SDK cannot distinguish the two. Re-provision the Lighter auth token, or run siweLogin again on Ondo. See Account access
  • 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 market doesn’t exist on the specified provider. Check:
  • Use getMarkets() to list valid marketId values for the provider
  • getMarket also throws this code when the market exists but is not active
  • getQuote throws this code when no market matches symbol and type, or when the provider returned no live price
  • GET /markets and GET /marketsContext return it when marketIds matches no market

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)

Reserved. The code is defined, but no provider currently returns it.

AccountNotFound (2026)

The address has not been onboarded on the specified provider. Check:
  • For Hyperliquid, the first deposit creates the account; offer getDepositFlow. Account data reads and account-channel subscriptions run the preTransferCheck probe, then throw this code before reading account data or opening a venue subscription
  • For Lighter, the first deposit creates the account. Applicable data reads throw this code for a wallet with no Lighter account; credential and setup checks can take precedence. See Account access
  • For Ondo, a missing or expired session does not raise this code: getAccount returns a logged-out account instead
  • 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
  • Lighter accepts integrator approvals and integrator-routed orders only from Plus or Premium accounts (venue code 21520). The SDK reports it as SetupRequired. Re-run checkSetup, execute the accountType step (Plus or Premium), then retry
  • For Ondo, this covers a withdrawal destination that is not yet in the Ondo address book (withdrawal_address_not_found) — run addWithdrawalAddress, then request a new withdrawal plan
  • For Hyperliquid, this covers a builder-fee approval the venue reports as missing — complete approveBuilderFee
  • For Lighter and Ondo, a data read with no stored credential throws this code, and the SDK sends no request to the venue. Run the setup action with gatesAccountReads: true: registerApiKey on Lighter, siweLogin on Ondo. See Account access
  • Meta referral endpoints return it for a code that is unavailable, already owned, or already attached — pick another code; re-running checkSetup does not help

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 sendAsset, modifyOrder, or updatePositionMargin methods and no choice setup step; 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)

Either the request failed before any response (SDK-side network failure such as a thrown fetch, DNS failure, or connection reset), or the backend returned HTTP 500 for an internal fault. 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:
  • An execution result with this code has status Indeterminate when the venue gave a transport failure, a 5xx answer or an unreadable body
  • 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)

An action was submitted but its outcome could not be confirmed in time (server-side verification timeout). The action may still have landed. An execution result with this code has status Indeterminate. Check:
  • Read order and position state before retrying
  • Do not blindly re-submit the action

ValidationError (2002)

The request failed validation, on the backend or in the SDK. Check:
  • The parameters and decimals are within the allowed ranges; the message says which one is not

SDKError (2005)

A client-side precondition failed, for example a provider that is not registered or a setup that is incomplete. The message identifies it. This code is raised in the SDK and never appears on the wire.

RouteNotFound (2060)

The HTTP method or path does not exist. The body reads Route <METHOD> <url> not found and carries no tool.

DefaultError (2000)

An unexpected backend error, or a non-JSON error body the SDK could not parse.

Ondo venue error mapping