Sui RPC timeouts are often caused by heavy queries exceeding QueryWeight limits, transport-level timeouts, or node overload. This article explains the differences between JSON-RPC, gRPC, and GraphQL transports, how QueryWeight affects request execution, and provides a runnable Node.js example with timeout and retry logic. It also includes a troubleshooting checklist and guidance on endpoint failover.
Direct Answer: Why Sui RPC Requests Time Out
Sui RPC timeouts occur when a request takes longer than the client or server allows, often because the query is too heavy (exceeding Sui's QueryWeight limits), the transport (JSON-RPC vs gRPC) has different timeout characteristics, or the node is overloaded. The most common cause is requesting too much data in a single call—for example, fetching a transaction block with full options or paginating through thousands of dynamic fields without proper limits. This article explains the underlying mechanisms and provides concrete fixes, including setting explicit timeouts and retries in the @mysten/sui.js SDK, narrowing query options, and failing over between endpoints.
Unlike a rate limit (which returns a 429 or similar), a timeout can manifest as a transport-level error (e.g., 'ETIMEDOUT'), a 500/503 with 'request timed out', or a stall that eventually errors. Understanding the difference is key to choosing the right fix. For a deeper dive on rate limits, see our Sui RPC rate limits and (potentially) compute units article.
- Transport timeouts: client-side or server-side limits on connection or response time.
- QueryWeight: Sui's per-request compute billing that can cause heavy queries to be rejected or take too long.
- Node overload: shared or under-provisioned nodes may not respond within your timeout window.
Sui RPC Transports: JSON-RPC, gRPC, and GraphQL
Sui offers multiple RPC transports, each with different timeout and framing characteristics. The primary transport is JSON-RPC over HTTP, which is synchronous and request-response. Timeouts are typically set on the HTTP client (e.g., 30 seconds) and on the server side (e.g., 60 seconds). If a query takes longer than the server's timeout, you may get a 500 or 503 with a 'request timed out' message.
gRPC is a binary protocol that supports streaming and has built-in deadlines. Sui's gRPC interface (used by the Sui Fullnode) allows for more efficient streaming of large datasets, such as checkpoint data. gRPC timeouts are set via context deadlines, and the protocol handles cancellation more gracefully. For low-latency streaming, gRPC is often preferred, but it requires a gRPC client and is not as widely supported as JSON-RPC.
GraphQL (Sui's RPC 2.0) is an emerging option that allows clients to request exactly the fields they need, reducing payload size and potential timeouts. As of 2026, it is still in development (see the RPC 2.0 issue on GitHub), but it promises to mitigate timeout issues by avoiding over-fetching.
- JSON-RPC: simple, widely supported, but synchronous and prone to timeouts on heavy queries.
- gRPC: binary, streaming, with deadlines; better for large data transfers.
- GraphQL: field selection reduces payload, but not yet stable.
QueryWeight: How Sui Bills Compute Per Request
Sui nodes use a QueryWeight mechanism to limit the computational cost of each RPC request. Each query type has a weight, and the node has a maximum weight per request and per second. If a request exceeds the per-request weight, it may be rejected with an error like 'Query is too heavy' or it may take so long that it times out. The exact limits are documented in the Sui documentation and vary by node configuration.
Heavy queries that commonly cause timeouts include:
getTransactionBlock with showInput: true, showEffects: true, and showEvents: true—this can return a massive payload.
getCoins or multiGetCoins with a large limit (e.g., 1000) and no pagination.
getDynamicFields with a large limit and deep recursion.
getCheckpoint with showContents: true for a checkpoint with many transactions.
To avoid timeouts, you should narrow the options to only what you need. For example, use showEffects: false if you only need the digest, or use multiGetCoins with a smaller limit and paginate using nextCursor.
- QueryWeight is per-request and per-second; exceeding it can cause timeouts or errors.
- Always request only the fields you need.
- Use pagination to break large queries into smaller chunks.
Diagnosing Sui RPC Timeouts
To diagnose a timeout, start by measuring the response time of a simple query versus a heavy one. Use curl with timing flags to see where the delay occurs. For example:
curl -w "time_total: %{time_total}s\n" https://fullnode.mainnet.sui.io/ -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"sui_getChainIdentifier","params":[],"id":1}'
If the simple query responds quickly but a heavy query times out, the issue is likely QueryWeight or payload size. If even simple queries time out, the node may be overloaded or your network connection is slow.
Also check the node's synchronization status. If the node is lagging behind the network, it may not have the data you're requesting, causing it to hang. Compare the latest checkpoint or epoch with an independent explorer like Sui Explorer to see if your node is behind.
- Use
curl -wto measure total time and time to first byte. - Compare simple vs heavy queries to isolate the cause.
- Check node sync status against an independent explorer.
Fixing Timeouts: SDK Configuration and Retry Patterns
The @mysten/sui.js TypeScript SDK allows you to set a timeout on the JSON-RPC client. For example, you can create a custom JsonRpcProvider with a fetch function that includes an AbortController with a timeout. Here's a runnable Node.js example that sets a 10-second timeout and implements exponential backoff retry:
import { SuiClient, getFullnodeUrl } from '@mysten/sui.js/client';
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function rpcWithRetry(client, method, params, { timeoutMs = 10000, retries = 3 } = {}) {
for (let attempt = 0; attempt < retries; attempt++) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const result = await client.call(method, params, { signal: controller.signal });
clearTimeout(timeout);
return result;
} catch (error) {
clearTimeout(timeout);
if (attempt === retries - 1) throw error;
const delay = Math.pow(2, attempt) * 1000;
console.log(`Attempt ${attempt + 1} failed: ${error.message}. Retrying in ${delay}ms`);
await sleep(delay);
}
}
}
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
// Example: get a transaction block with minimal options
const txDigest = 'your_tx_digest_here';
const result = await rpcWithRetry(client, 'sui_getTransactionBlock', [txDigest, { showEffects: false }]);
console.log(JSON.stringify(result, null, 2));
// Expected output: a JSON object with the transaction block data, or an error after retries.
In addition to timeouts, you should narrow your query options. For getTransactionBlock, use showEffects: false unless you need effects. For getCoins, use a limit of 50 or 100 and paginate with nextCursor. For getDynamicFields, use a limit and avoid deep recursion.
If you're using gRPC, set a deadline on the context. For example, in Node.js with the @grpc/grpc-js library, you can set a deadline of 10 seconds. gRPC also supports cancellation, which can be useful for long-running streams.
- Set explicit timeouts on your HTTP client to avoid hanging indefinitely.
- Implement exponential backoff retry for transient failures.
- Narrow query options to reduce payload size and compute weight.
- Use
multiGetCoinsinstead of loopinggetCoinsfor multiple coin types.
Runnable example: timeout, backoff retry, and health check with the Sui TypeScript SDK
The following Node.js script demonstrates a complete, self-contained example using @mysten/sui/client. It creates a SuiClient, wraps a getObject call in a timeout with exponential backoff retry, and performs a simple endpoint health check by fetching the chain identifier. The example is designed to be run as-is after installing the SDK.
When you run this script, you should see output similar to the following. First, the health check prints the chain identifier (a hex string). Then, the getObject call returns a JSON object with the requested object's details, including its type and digest. If the endpoint is down or the request times out, the retry logic logs each failed attempt and eventually throws an error, which is caught and printed.
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function rpcWithRetry(client, method, params, { timeoutMs = 10000, retries = 3 } = {}) {
for (let attempt = 0; attempt < retries; attempt++) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const result = await client.call(method, params, { signal: controller.signal });
clearTimeout(timeout);
return result;
} catch (error) {
clearTimeout(timeout);
if (attempt === retries - 1) throw error;
const delay = Math.pow(2, attempt) * 1000;
console.log(`Attempt ${attempt + 1} failed: ${error.message}. Retrying in ${delay}ms`);
await sleep(delay);
}
}
}
async function healthCheck(client) {
try {
const chainId = await rpcWithRetry(client, 'sui_getChainIdentifier', []);
console.log('Health check passed. Chain ID:', chainId);
return true;
} catch (error) {
console.error('Health check failed:', error.message);
return false;
}
}
async function main() {
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
// Health check
const healthy = await healthCheck(client);
if (!healthy) {
console.error('Endpoint is not healthy. Exiting.');
process.exit(1);
}
// Example: get an object with retry and timeout
const objectId = '0x0000000000000000000000000000000000000000000000000000000000000001';
try {
const result = await rpcWithRetry(client, 'sui_getObject', [objectId, { showType: true }]);
console.log('Object data:', JSON.stringify(result, null, 2));
} catch (error) {
console.error('Failed to fetch object after retries:', error.message);
}
}
main();Common Failures and Fixes
Here are common timeout scenarios and their fixes:
Scenario 1: getTransactionBlock with full options times out. Fix: Set showInput: false, showEffects: false, showEvents: false unless you need them. If you need effects, consider fetching them separately.
Scenario 2: getCoins with a large limit times out. Fix: Use a limit of 50-100 and paginate with nextCursor. Or use multiGetCoins with a list of coin object IDs.
Scenario 3: getDynamicFields with a large limit times out. Fix: Reduce the limit and paginate. Avoid recursive calls that fetch all nested dynamic fields.
Scenario 4: Checkpoint queries with showContents: true time out. Fix: Set showContents: false if you only need the checkpoint summary.
Scenario 5: All queries time out, even simple ones. Fix: Check your network connection, the node's health, and whether the node is synced. Consider switching to a different endpoint or using a provider with better performance (see our Sui RPC latency and performance article).
- Always use the smallest options object that meets your needs.
- Pagination is your friend—never request more than 100 items at a time.
- If a node is consistently slow, failover to another endpoint.
Tradeoffs and Limitations
While gRPC offers better streaming, it requires a more complex client setup and may not be supported by all providers. JSON-RPC is simpler but more prone to timeouts on heavy queries. GraphQL is promising but not yet stable.
QueryWeight limits are not always documented precisely; they can vary by node configuration and provider. For provider-specific limits, refer to their documentation. OnFinality's RPC pricing page provides details on our service, but we do not publish specific latency or throughput numbers.
Retry patterns can mask underlying issues. If a query consistently times out after retries, it's better to optimize the query than to increase retry counts. Also, be mindful of rate limits—retrying too aggressively can trigger rate limiting, which is a different problem (see our Sui RPC rate limits article).
- gRPC is not a silver bullet; it requires client-side support.
- QueryWeight limits are not always public; test with your provider.
- Retries should be used for transient errors, not for heavy queries.
Next Steps and Further Reading
To get the most out of Sui RPC, start by implementing the retry pattern and narrowing your queries. If you're building a production application, consider using a reliable RPC provider like OnFinality's API service, which offers high availability and failover. You can also consult our Sui RPC guidance (RPC Assistant) for quick tips.
For a broader understanding of Sui, see our Sui network page. And don't forget to explore other articles on the OnFinality Learn hub for more troubleshooting guides.
- Implement timeouts and retries in your client code.
- Optimize your queries to stay within QueryWeight limits.
- Use a provider with multiple endpoints for failover.
- Monitor your node's sync status and performance.