Every failure the SDK raises is an InteropError with a kind. Branch on the class, the kind or the hub’s code, never on a message.

The error classes

All entry points share one copy of these classes, so instanceof works whichever entry point threw. isAbortError(error) is also true for a platform AbortError.

HubError

Two getters sort the codes that are not about the request:
  • isCredentialRefused: the gateway refused your credential (401: EPX0001 a missing or malformed key, EPX0002 an unknown, revoked or expired one, EPX0008 or EPX0009 a malformed or unknown project id), or a page’s origin that is not on the project’s list (403, EPX0010). An operator matter: retrying cannot fix it, and refused credentials count toward the gateway’s lockout of the caller’s IP (EPX0081).
  • isClientBug: a framework code that means the request itself was malformed (CLIENT_BUG_CODES).
Every code is listed in the Error reference.

What to tell the user

adviseError(error) sorts any error the SDK throws into an action, who it is for, and a default message:
audience is operator for the codes that describe your key, your project, your origins or your client: the user only needs to hear that the service is unavailable. DEFAULT_ERROR_MESSAGES holds the default English copy. Pass the route when you know it: adviseError(error, { route: "orderOpenFor" }) reads availability answers and a HubUnavailableError as resubmit, because on a submission they say nothing about whether the order exists.

Retrying

What the SDK already does

  • Timeouts. Every transport waits a little longer than the gateway, so the gateway’s own answer ends a slow call: hubTransport and projectTransport wait 25 s on quotes, 12 s on reads and 15 s on submissions; proxyTransport waits 30 s, 15 s and 35 s, longer than your proxy.
  • Submissions. When an order submission’s answer is lost (a timeout, a dropped connection, the gateway’s EPX0050–EPX0053), the SDK looks the order up by its derived hub order id, then re-posts the same body once. See Lost answers.
  • Tracking. Status polls ride out availability and busy answers, waiting at least retryAfterMs.
  • Quotes. executeQuote re-quotes when too little time is left to sign. See How long a quote lives.
  • Catalog. When the hub cannot be reached, the last good copy is used.
Everything else throws once, and the rest is your call.

Your rules

  • retryable is true: back off and retry. On a rate limit (EIN0080, the gateway’s 429), wait retryAfterMs first.
  • isCredentialRefused is true: stop, and alert your operators. Check the key or project id, and the project’s origin list, in the hub dashboard. Don’t retry meanwhile: 20 refused credentials within a minute lock the caller’s IP out of every call for the rest of that minute (EPX0081).
  • A terminal code (0001–0049): never retry unchanged. Fix the input, or quote again.
  • Once the user has sent a transaction for a quote (the wrap-native and swap-token lanes), never quote again or ask them to sign again for it: the deposit is on chain. Follow the order instead, with client.findOrderByTx(sourceChain, transactionHash) if you lost its id.
  • A HubUnavailableError on a POST may have been accepted. For quotes, just ask again. For submissions, the SDK’s recovery has already run: an OutcomeUnknownError means follow the order.
  • Your own abort: do not retry.

In the browser

Through your proxy, the browser gets:
  • HubError for the hub’s refusals about the request, relayed unchanged, and for the gateway’s rate limits and outages (429, 5xx), with their status and Retry-After;
  • ProxyError for everything about your key or your proxy: the proxy never relays a refused key. It reports it to you (onError) and answers PXY0503, as it does for history while its subject canary fails; a client bug or another refusal about the integration is PXY0500;
  • HubUnavailableError when the proxy cannot be reached, and a ProxyError PXY0502 when the proxy cannot reach the hub.
With a project id, the browser gets the gateway’s refusals directly, as HubErrors with an EPX code and a real status: EPX0010 (403), for one, means the page’s origin is not on your project’s list. GET /orders, the one route not open to a project id, never gets that far: projectTransport refuses it before calling, with an InteropError (invalid-input).