Troubleshooting
Use this page when SDK v1 reads or writes do not behave the way you expect. Most production issues fall into one of six buckets: missing receipt handling, stale follow-up reads, duplicate submits, transport failures, an SDK version that predates a new market listing, or workflow assumptions that belong to SDK v2 or GraphQL instead.
The SDK returned a transaction hash, but my UI is still pending
That is expected. SDK v1 order methods submit the transaction and return the hash. They do not wait for the receipt for you.
Recommended pattern:
- Persist the transaction hash as soon as you receive it.
- Wait for the receipt with
sdk.publicClient.waitForTransactionReceipt. - Only after the receipt succeeds should you start polling positions, orders, or trade history.
If the client reloads before confirmation, recover from the saved transaction hash instead of resubmitting.
The transaction succeeded, but the order or position is not visible yet
Treat follow-up reads as eventually consistent.
- Transaction inclusion, order creation, keeper execution, and indexed history do not complete at the same time.
orders.getOrders()shows active on-chain orders, but trade history comes from Subsquid and can lag behind live chain state.- If you need a coherent account snapshot, refresh one
marketsInfoDataandtokensDatasnapshot and reuse it across position and order reads.
I accidentally submitted the same action twice
SDK v1 does not support idempotency keys or client order IDs.
Recommended client behavior:
- Disable repeat submit actions while a transaction is pending.
- Persist the last in-flight transaction hash per user action.
- Require an explicit retry action after a failure instead of auto-resubmitting writes.
Cancel or replace shows unexpected order state
This is usually a race between an old UI snapshot and current on-chain order keys.
- Refetch active orders immediately before calling
cancelOrders. - Cancel by the latest on-chain order keys, not by cached UI state from an earlier poll.
- After cancel or replace, refetch orders again instead of assuming the previous list is still valid.
Read calls fail with timeouts or transient RPC errors
The default SDK-created viem transports set retryCount: 0, and SDK multicalls use a 40000 ms timeout.
Recommended client behavior:
- Add retry and backoff around safe read paths.
- Keep write submission separate from read retries so you do not duplicate transactions.
- Consider supplying your own viem clients if you need a custom transport strategy.
If you inject a custom viem publicClient, make sure it includes BATCH_CONFIGS from @gmx-io/sdk/configs/batch. The SDK-created default clients already do this for you, but custom clients do not inherit it automatically.
Without the SDK batch config, large GMX multicalls may be fragmented into many smaller requests instead of one client-side multicall. Common symptoms include unexpectedly high RPC request counts, public RPC rate limits, and browser-side transport failures during otherwise normal read or order-helper flows.
Market reads fail with a missing pre-built keys error
In SDK versions before 2.4.0, sdk.markets.getMarketsInfo() can throw No pre-built hashed config keys found for the market 0x… after GMX lists a new market. SDK v1 builds its market list at runtime from your oracleUrl and on-chain data, but these versions can read a market's config only with hashed keys that ship inside the SDK package. If your installed version has no keys for a newly listed market, the whole call fails, not only the read for that market. The quick order helpers fail the same way when you don't pass marketsInfoData and tokensData.
For example, SDK 2.2.1 and 2.3.1 fail on Arbitrum after the NEAR/USD, XRP/USD, ZEC/USD, and HYPE/USD [USDG-USDG] markets were listed on October 16, 2026. Version 2.3.2 adds the keys for these markets.
To resolve the error, upgrade @gmx-io/sdk to 2.4.0 or later. These versions hash the keys of a market with no packaged keys at runtime, so a newly listed market no longer causes this error. A market whose tokens aren't in your installed package is skipped with a console warning, so upgrade to read it. If you can't upgrade right away, hide the new markets with an isListed: false override. The error names only the first market it can't read, so add every newly listed market, not only that one. Use checksummed addresses, in the same casing the error prints, because override keys are case-sensitive.
const sdk = new GmxSdk({
chainId: 42161,
rpcUrl: "https://arb1.arbitrum.io/rpc",
oracleUrl: "https://arbitrum-api.gmxinfra.io",
subsquidUrl: "https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql",
markets: {
"0x54bc430A788DFFD4ae35e751E10899489f5a953f": { isListed: false }, // NEAR/USD [USDG-USDG]
"0xc9ED746D613eEfC0F196658f6030e90482593004": { isListed: false }, // XRP/USD [USDG-USDG]
"0x2CeEafcA180B07C9d1d2f1086f8f59AAFd282832": { isListed: false }, // ZEC/USD [USDG-USDG]
"0x162C091263fBb3dfAf1D55DCC979533EB341DA5f": { isListed: false }, // HYPE/USD [USDG-USDG]
},
});
The SDK skips hidden markets entirely, so it doesn't return market data or positions for them. Remove the override after you upgrade. For the override format, see Customizing market availability.
A limit order did not simulate before submit
That can be current behavior, not necessarily a bug.
- Market increases simulate by default unless you opt out.
- Limit increases skip simulation in the current implementation.
- Market swaps simulate by default, while
Limit Swaporders do not. createDecreaseOrder()currently skips simulation.
If your UI exposes these flows, validate inputs before submission and surface chain-level errors clearly.
Next steps
- Use the Integration guide for exact SDK v1 workflows.
- Use Getting Started for constructor and module reference.
- Use SDK Overview when you need to choose between SDK v1 and SDK v2.