Skip to Content
Symmio Trading-SDK — the SDK surface for builders on Arbitrum

Errors

@symmio/trading-core uses one error class hierarchy for SDK-level failures and lets viem’s own errors pass through unwrapped. Two vocabularies — the SDK’s typed SymmError for configuration / HTTP / validation, and viem’s BaseError tree for on-chain and transport failures.

Class hierarchy

SymmError (all SDK-level failures — kind + code + message) └─ SymmApiError (HTTP failures — adds status, url, method, responseData)

SymmError

import { SymmError } from "@symmio/trading-core"; class SymmError extends Error { readonly name = "SymmError"; readonly kind: "config" | "api" | "validation"; readonly code: string; // e.g. "UNSUPPORTED_CHAIN" constructor(kind, code, message, options?); }
  • kind — broad classification for error-handling branches.
    • "config" — SDK configuration issues (missing chain, no wallet client).
    • "api" — REST / GraphQL / WebSocket transport failures.
    • "validation" — input validation (missing required parameter).
  • code — specific identifier within the kind ("UNSUPPORTED_CHAIN", "MISSING_USER").

SymmApiError extends SymmError

Every HTTP failure surfaces as SymmApiError with kind === "api":

class SymmApiError extends SymmError { readonly status: number; // HTTP status (0 when there was no response: network failure or timeout) readonly statusText: string; readonly responseData: unknown; // raw response body (solver / hedger error payload) readonly url: string; readonly method: string; readonly retryAfterMs: number | null; // Retry-After delay in ms, when the response carried one }

SymmApiError.fromAxios(err, { code, baseURL }) builds one from an axios error, preserving the original as cause and composing a single-line message: "<code>: <axios message> (<METHOD> <URL> → <status> <statusText>)".

  • retryAfterMs is parsed from a Retry-After response header, in either delay-seconds or HTTP-date form. It is null when there is no usable header. A cross-origin browser reads the header only when the server lists it in Access-Control-Expose-Headers, so null means “unknown”: still back off before retrying a 429 or 503.
  • cause keeps the axios request config, request headers and body included (a Pools action’s Authorization: Bearer access token, for example). Redact it before forwarding the error to third-party logging.
  • Gasless HTTP failures are the exception: their cause is a sanitized snapshot (name, message, code, status, method, url) with no request headers or body. Their responseData is still the raw response body, and it can echo request fields — see Reading the vendor detail.

Handling

import { SymmError, SymmApiError } from "@symmio/trading-core"; import { BaseError, ContractFunctionRevertedError, UserRejectedRequestError } from "viem"; try { await editAccountName(config, { account, name }); } catch (err) { if (err instanceof SymmApiError) { console.error(`HTTP ${err.status}`, err.responseData); return; } if (err instanceof SymmError) { if (err.kind === "config") console.error("Setup:", err.code, err.message); if (err.kind === "validation") console.error("Bad input:", err.code, err.message); return; } if (err instanceof BaseError) { const revert = err.walk((e) => e instanceof ContractFunctionRevertedError); if (revert instanceof ContractFunctionRevertedError) { console.log("revert reason:", revert.reason); return; } if (err.walk((e) => e instanceof UserRejectedRequestError)) return; } throw err; }

Alternatively check err.name === "SymmError" across realms.

Catalogue

Codes are stable strings. Group per kind. New codes may appear at any minor version; unknown code should fall through to a generic branch.

kind: "config"

CodeThrown byMeaning
AFFILIATE_ADDRESS_REQUIREDcreateConfigA supported chain’s symmioConfig affiliate is missing.
NO_CHAINS_CONFIGUREDcreateConfigNo supported chains in the built-in registry.
UNSUPPORTED_CHAINConfig.getChainConfig, most reads/writesChain id not in the config registry.
NO_WALLET_CLIENTConfig.getWalletClient, every writecreateConfig was called without a getWalletClient resolver.
NO_WEBSOCKETConfig.getWebSocketConstructor, every streamNo webSocketConstructor and no globalThis.WebSocket.
PRICES_WS_NOT_CONFIGUREDwatchEnigmaPricesChain lacks a price-service WebSocket URL.
MUON_URLS_NOT_CONFIGUREDgetMuonRequestChain lacks Muon oracle URLs.
LISTING_NOT_CONFIGUREDresolveListingService, getListingMarketsChain has no listing backend configured.
INVENTORY_NOT_CONFIGUREDresolveInventoryService, getInventoryTvlChain has no inventory service configured.

kind: "validation"

CodeThrown byMeaning
MISSING_USERgetUserSubAccounts, getUserSubAccountsAddresses, getSubAccountsCountOfUseruser parameter required.
MISSING_ACCOUNTSeveral read/write actionsaccount parameter required.
MISSING_OWNEROwnership-scoped readsowner parameter required.
MISSING_PARTY_AParty-A-scoped readspartyA parameter required.
MISSING_QUOTE_IDQuote-scoped readsquoteId parameter required.
COLLATERAL_DECIMALS_UNSUPPORTEDcollateralToCore18, core18ToCollateral, withdrawAutoCollateral decimals are not an integer from 0 to 18.
AMBIGUOUS_FUNDINGprepareInstantOpenParams, getInstantOpenFeesBoth initialMargin and fund, or both availableBalance and fund, were passed.
INITIAL_MARGIN_REQUIREDprepareInstantOpenParams, getInstantOpenFeesNeither initialMargin nor fund was passed.
FULL_BALANCE_UNSUPPORTEDprepareInstantOpenParams, getInstantOpenFeesfund or automatic-fallback availableBalance was used on a non-lowcap solver.
QUOTE_CONSTRAINT_VIOLATEDFull-balance sizing in prepareInstantOpenParams / getInstantOpenFeesThe full-balance-sized quantity violates a published market constraint.

kind: "api"

Selected — many more surface as SymmApiError with a _FAILED code per action:

| Code | Thrown by | | ------------------------------------------------------- | -------------------------------------------- | -------------------------------------------- | | FETCH_MARKETS_FAILED | getMarkets | | FETCH_LOCKED_PARAMS_FAILED | getLockedParams | | FETCH_NOTIONAL_CAP_FAILED | getNotionalCap | | FETCH_NOTIONAL_CAP_ALL_FAILED | getNotionalCapAll | | FETCH_SOLVER_REVENUE_FAILED | getSolverRevenue | | FETCH_SOLVER_ERROR_CODES_FAILED | getSolverErrorCodes | | FETCH_MUON_REQUEST_FAILED | getMuonRequest | | FETCH_ENIGMA_PRICE_SERVICE_HEALTH_FAILED | getEnigmaPriceServiceHealth | | FETCH_ENIGMA_PRICE_SERVICE_METADATA_FAILED | getEnigmaPriceServiceMetadata | | FETCH_ENIGMA_PRICE_SERVICE_PRICES_BY_NAMES_FAILED | getEnigmaPriceServicePricesByNames | | FETCH_ENIGMA_PRICE_SERVICE_PRICES_BY_ADDRESSES_FAILED | getEnigmaPriceServicePricesByAddresses | | FETCH_ENIGMA_PRICE_SERVICE_SYMBOLS_INFO_FAILED | getEnigmaPriceServiceSymbolsInfo | | GET_INSTANT_OPENS_FAILED | getInstantOpens | | GET_INSTANT_CLOSES_FAILED | getInstantCloses | | GET_INSTANT_OPEN_QUOTE_ID_FAILED | getInstantOpenQuoteId | | SEARCH_NOTIFICATIONS_FAILED | searchNotifications | | SET_TPSL_REJECTED | setQuoteTpSl (handler returned failure) | | DELETE_TPSL_REJECTED | deleteQuoteTpSl (handler returned failure) | | FETCH_QUOTE_TPSL_FAILED | getQuoteTpSl | | TPSL_SOCKET_ERROR | watchTpSlNotifications | | NOTIFICATIONS_SOCKET_ERROR | watchNotifications | | FETCH_LISTING_MARKETS_FAILED | getListingMarkets | | FETCH_INVENTORY_TVL_FAILED | getInventoryTvl | | FETCH_LISTING_MARKET_DETAIL_FAILED | getListingMarketDetail | The pool-detail request failed. | | FETCH_POOL_TRANSACTIONS_FAILED | getPoolTransactions | The pool transaction-history request failed. | | PRICES_SOCKET_ERROR | watchEnigmaPrices |

viem errors (on-chain / transport)

Contract reverts, gas failures, wallet rejections, RPC unreachable — all bubble up as viem errors. Use BaseError.walk() to inspect the cause chain.

import { BaseError, ContractFunctionRevertedError } from "viem"; if (err instanceof BaseError) { const revert = err.walk((e) => e instanceof ContractFunctionRevertedError); if (revert) console.log(revert.reason); }

React

@symmio/trading-react normalizes both — every hook wraps queryFn / mutationFn and runs failures through normalizeSymmError → SymmioRequestError (discriminated by kind). See the React errors page.

Last updated on