Skip to main content

Error classes

Every error thrown by the SDK is an instance of OneSwapError:
import {
  ConflictError,    // 409 — active intent or other request conflict
  ActiveSwapIntentError, // 409 — active swap intent already exists (exposes existingIntentId)
  AmbiguousPoolPairError, // 409 — token pair maps to multiple pools
  AuthError,        // 401 — invalid or missing API key or wallet token
  ValidationError,  // 400 — bad parameters
  SettlementSafetyError, // 400 — swaps.create blocked by the pre-payout safety guard (exposes code/reason)
  NotFoundError,    // 404 — pool/intent/party not found
  RateLimitError,   // 429 — too many requests
  SlippageError,    // swap output below minimum
  PriceImpactError, // created swap later hit price_impact_exceeded during wait()
  InsufficientLiquidityError, // swap refunded because the pool was too thin
  InsufficientAmountError, // swap refunded because the deposit was below required network cost
  OutputFailedError,// swap output transfer failed
  SwapFailedError,  // generic terminal swap failure
  RefundFailedError,// swap refund failed
  ExpiredError,     // backend marked the intent expired or cancelled
  NetworkError,     // transport-level failure before an HTTP response
  TimeoutError,     // client-side request timeout or wait() polling deadline
  ServerError       // 500 — something went wrong on our end
} from '@oneswap/sdk'

Handling swap errors

try {
  const swap = await client.swaps.create({ ... })
  const result = await swap.wait()
} catch (err) {
  if (err instanceof SlippageError) {
    console.log('Swap refunded due to slippage')
  } else if (err instanceof PriceImpactError) {
    console.log('Swap rejected — price impact exceeds the maximum allowed')
  } else if (err instanceof InsufficientLiquidityError) {
    console.log('Swap refunded due to insufficient liquidity')
  } else if (err instanceof InsufficientAmountError) {
    console.log('Swap refunded because the input amount was too small')
  } else if (err instanceof OutputFailedError) {
    console.log('Swap payout failed — inspect the terminal intent status')
  } else if (err instanceof RefundFailedError) {
    console.log('Swap refund failed — manual intervention is required')
  } else if (err instanceof SwapFailedError) {
    console.log('Swap failed — inspect the intent before retrying')
  } else if (err instanceof ExpiredError) {
    console.log('Backend marked the intent expired or cancelled')
  } else if (err instanceof AmbiguousPoolPairError) {
    console.log(err.candidates)
  } else if (err instanceof ActiveSwapIntentError) {
    // An active intent already exists — resume it instead of creating a duplicate
    const status = await client.swaps.getStatus(err.existingIntentId)
  } else if (err instanceof ConflictError) {
    console.log(err.message)
  } else if (err instanceof AuthError) {
    console.log('Check your API key and repeat wallet authentication if needed')
  } else if (err instanceof SettlementSafetyError) {
    // swaps.create was blocked by the pre-payout safety guard before any deposit.
    // No intent was created. Reduce the swap size or wait a few minutes, then retry.
    console.log(err.message, err.code, err.reason)
  } else if (err instanceof ValidationError) {
    console.log(err.message)
  } else if (err instanceof TimeoutError) {
    // Client-side deadline (one request, or the wait() polling loop).
    // The swap may still be settling — resume with client.swaps.getStatus(id).
    console.log('Client timed out; the swap may still be settling')
  } else if (err instanceof NetworkError) {
    console.log(err.message)
  }
}
Check ActiveSwapIntentError before ConflictError — it is a subclass, so the generic ConflictError branch would otherwise swallow it. It exposes existingIntentId and existingIntentSource so you can resume or cancel the existing intent rather than parsing the raw response. PriceImpactError is raised by swap.wait() after a created intent reaches terminal status price_impact_exceeded. Quote-time or create-time price-impact rejections are plain ValidationError responses because the API returns HTTP 400.

Settlement safety guard

swaps.create runs a pre-payout safety guard. When a swap of this size or timing would be blocked at settlement, the API returns HTTP 400, the SDK throws SettlementSafetyError, and no intent is created — no deposit happens. SettlementSafetyError is a subclass of ValidationError, so check it before the generic ValidationError branch.
try {
  const swap = await client.swaps.create({ ... })
} catch (err) {
  if (err instanceof SettlementSafetyError) {
    // No intent was created. Reduce the swap size or wait a few minutes, then retry.
    console.log(err.message)        // user-facing sentence
    console.log(err.code)           // e.g. 'output_velocity_exceeded'
    console.log(err.reason)         // technical detail (bps/thresholds)
  }
}
To avoid hitting this on create, inspect quote.settlementSafety first — it is null when the swap is clear to proceed, and carries the same { code, reason, message } when a swap that size would be blocked.

Error properties

All errors have:
err.message    // human-readable description
err.statusCode // HTTP status (0 for lifecycle or transport errors)
err.response   // raw response body from the API
AmbiguousPoolPairError also exposes err.candidates, which lists the matching pools and token admins you can use to retry with poolId, fromAdmin, or toAdmin. SettlementSafetyError also exposes err.code (the machine-readable block reason) and err.reason (the technical detail), in addition to the user-facing err.message.

Common scenarios

ErrorWhenWhat to do
AmbiguousPoolPairErrorThe same token symbols exist under multiple adminsRetry with poolId or both token admin fields
ActiveSwapIntentErrorAn active swap intent already exists for this wallet, pool, and input token (subclass of ConflictError)Resume the existing intent via err.existingIntentId, or cancel it before creating another
ConflictErrorSome other request conflict not covered by a more specific subclassInspect err.message
AuthErrorAPI key is wrong, missing, or the wallet token is invalid/expiredCheck your key and refresh wallet auth
ValidationErrorBad params or preflight validation failed (missing field, invalid amount, token/pool mismatch)Fix the request
SettlementSafetyError (400)swaps.create was blocked by the pre-payout safety guard; no intent was created (subclass of ValidationError)Reduce the swap size or wait a few minutes, then retry. Inspect err.code/err.reason, or check quote.settlementSafety first
NotFoundErrorPool or intent doesn’t existCheck the ID
RateLimitErrorToo many requestsBack off and retry
SlippageErrorPrice moved during swapRetry with higher tolerance or try again
PriceImpactErrorAn already-created swap later reached terminal status price_impact_exceeded during executionReduce swap size or add liquidity to the pool before retrying
InsufficientLiquidityErrorThe pool could not fill the swapRetry later or reduce size
InsufficientAmountErrorA created swap was refunded because the live network cost exceeded the depositIncrease the amount and create a new intent
OutputFailedErrorOutput payout failed or the swap party had no output holdingsInspect the terminal intent status before retrying
SwapFailedErrorThe swap reached a generic terminal failure stateInvestigate the intent before retrying
RefundFailedErrorThe refund path failed after a swap errorManual intervention is required
ValidationError (400)Quote or intent creation failed because price impact already exceeded the server capReduce swap size or add liquidity before retrying
ValidationError (400)Quote or intent creation failed because the amount was below the estimated network costIncrease the swap amount. The error response includes minimumAmount.
ExpiredErrorThe swap intent expired after its current 24-hour deposit window, or swap.wait() observed a cancelled intentCreate a new intent if needed
TimeoutErrorA single SDK request timed out, or swap.wait() reached its local polling deadlineThe swap may still be settling — resume with client.swaps.getStatus(intentId), or increase timeout
NetworkErrorThe request failed before an HTTP response was receivedRetry after checking connectivity
ServerErrorOur problemRetry after a moment

Configuring wait behavior

const result = await swap.wait({
  pollInterval: 5000,   // check every 5 seconds (default: 3s)
  timeout: 600000       // give up after 10 minutes (default: 35min)
})
wait() is resilient to transient failures: a poll that fails with NetworkError, TimeoutError, ServerError (5xx), or RateLimitError is retried on the next interval rather than aborting the wait. Non-transient errors (AuthError, ValidationError, etc.) propagate immediately. Reaching timeout throws TimeoutError — a client-side deadline, not an on-chain expiry.