An Ethereum RPC timeout occurs when a client request to a node exceeds a time limit, often due to heavy queries like eth_getLogs, provider misconfiguration, or network issues. This article explains the root causes, shows how to set proper timeouts in ethers.js and viem, and provides retry patterns with backoff.
What Is an Ethereum RPC Timeout?
An Ethereum RPC timeout happens when your application sends a JSON-RPC request to an Ethereum node but does not receive a response within the time you (or your provider) have allocated. This is different from a 429 rate-limit error (which means you sent too many requests) or a node JSON-RPC error (which means the node processed the request but returned an error). A timeout means the request was either never processed or took too long to complete.
The most common causes are client-side provider timeouts (e.g., ethers.js default 30-second limit), heavy queries like eth_getLogs over long block ranges, and node-side bottlenecks such as the pending-tx lock in go-ethereum. Understanding these root causes is the first step to fixing them.
- Client-side timeout: your provider library (ethers.js, viem) aborts the request after a set duration.
- Node-side timeout: the Ethereum node itself (e.g., go-ethereum) has internal limits or locks that delay responses.
- Network timeout: the connection between your app and the RPC endpoint drops or is too slow.
Why Ethereum RPC Timeouts Happen: Provider Config and Node Behavior
In ethers.js, the default timeout for a JsonRpcProvider is 30 seconds (the timeout option). If your request takes longer, the provider throws a TIMEOUT error. In viem, the timeout option defaults to 10,000 ms (10 seconds) for publicClient methods. These defaults are often too short for heavy queries like eth_getLogs over a large range or eth_call on archive nodes.
On the node side, go-ethereum has method-level behavior that can cause timeouts. For example, eth_getLogs over a very large block range can take minutes, especially on archive nodes. Similarly, debug_ and trace_ methods are computationally expensive and can block the node's execution queue. The go-ethereum issue tracker records these problems: issue #31718 discusses RPC timeouts in version 1.15.x, and issue #23416 proposes adding a timeout parameter to RPC calls. These are documented as issue-tracker records, not as official guarantees.
Another common cause is the pending-tx lock: when you call eth_sendRawTransaction or eth_getTransactionCount with pending block, the node may lock the transaction pool, causing other requests to wait. This is referenced in geth issues and can lead to cascading timeouts.
- ethers.js default timeout: 30 seconds (configurable via
timeoutoption). - viem default timeout: 10,000 ms (configurable per call).
- go-ethereum
eth_getLogscan be slow over large ranges; consider paging. debug_andtrace_methods are expensive and may be disabled on public endpoints.- Pending-tx lock can block other RPC calls.
Multicall and Looped eth_call: Aggregate Timeouts
Multicall libraries like Multicall3 or 1inch's aggregator allow you to batch multiple eth_call requests into one. However, if the batch is too large or the underlying calls are heavy (e.g., reading from complex contracts), the single request can exceed the timeout. Similarly, looping over many eth_call requests in a for-loop can cause each to hit the timeout individually, leading to a poor user experience.
The key is to balance batch size and timeout. For example, a multicall with 100 simple balance checks might take 1-2 seconds, but a multicall with 50 complex DeFi operations could take 10+ seconds. If your provider timeout is 10 seconds, you'll get a timeout. You need to either increase the timeout or reduce the batch size.
- Multicall3 allows batching multiple
eth_callinto one request. - Large batches can exceed provider timeouts.
- Looped
eth_callcan cause multiple timeouts and rate limits. - Solution: split batches, increase timeout, or use a dedicated batch provider.
How to Set Timeouts and Retry Patterns in ethers.js and viem
In ethers.js, you can set a custom timeout when creating a JsonRpcProvider by passing a timeout option (in milliseconds). For example, new ethers.JsonRpcProvider(url, network, { timeout: 60000 }) sets a 60-second timeout. You can also set dupTimeout for duplicate request detection (default 10 seconds). For individual calls, you can use Promise.race with a timeout, but the provider-level timeout is simpler.
In viem, you can pass a timeout option to individual public client methods, like client.getLogs({ address, fromBlock, toBlock, timeout: 30_000 }). This overrides the default 10-second timeout for that call.
For retries, implement exponential backoff with jitter. Never retry state-changing transactions (like eth_sendRawTransaction) blindly, because you might duplicate the transaction. Instead, check the transaction receipt or use a nonce manager. For read-only calls, retrying is safe.
- ethers.js:
new ethers.JsonRpcProvider(url, network, { timeout: 60000 }). - viem:
client.getLogs({ ..., timeout: 30_000 }). - Retry with exponential backoff: wait 1s, 2s, 4s, etc., with jitter.
- Never retry state-changing transactions without checking nonce/receipt.
const { ethers } = require('ethers');
async function getLogsWithRetry(provider, filter, maxRetries = 3) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await provider.getLogs(filter);
} catch (error) {
if (error.code === 'TIMEOUT' && attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(`Timeout, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
attempt++;
} else {
throw error;
}
}
}
}
async function main() {
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY', undefined, { timeout: 30000 });
const filter = { fromBlock: 19000000, toBlock: 19001000, address: '0x...' };
try {
const logs = await getLogsWithRetry(provider, filter);
console.log(`Got ${logs.length} logs`);
} catch (error) {
console.error('Failed after retries:', error);
}
}
main();
// Expected output: either "Got N logs" or "Failed after retries: ..."Optimizing eth_getLogs and Avoiding Timeouts
The most common cause of Ethereum RPC timeouts is eth_getLogs over a large block range. For example, querying logs from block 1 to 19,000,000 is impossible on most public endpoints. The solution is to narrow the range and page by block windows. For instance, query logs in chunks of 10,000 blocks, and if the response is still too large, reduce the chunk size.
Another approach is to use eth_subscribe for real-time logs (newHeads, logs) instead of polling. This reduces the number of requests and avoids timeouts for ongoing events. For historical data, consider using a dedicated indexing service or a provider that supports batch requests.
Caching storage reads is also effective. If you repeatedly call eth_call for the same contract state, cache the result locally to avoid redundant RPC calls.
- Narrow block ranges: query in chunks of 10,000 blocks or less.
- Use
eth_subscribefor real-time logs instead of polling. - Cache storage reads to reduce repeated
eth_call. - Use batch JSON-RPC providers where supported (e.g., some providers allow batch requests).
// Example: paging eth_getLogs in chunks
const { ethers } = require('ethers');
async function getLogsInChunks(provider, address, fromBlock, toBlock, chunkSize = 10000) {
let logs = [];
for (let start = fromBlock; start <= toBlock; start += chunkSize) {
const end = Math.min(start + chunkSize - 1, toBlock);
const filter = { address, fromBlock: start, toBlock: end };
try {
const chunkLogs = await provider.getLogs(filter);
logs = logs.concat(chunkLogs);
console.log(`Fetched ${chunkLogs.length} logs from ${start} to ${end}`);
} catch (error) {
console.error(`Error fetching logs from ${start} to ${end}:`, error);
// Optionally retry with smaller chunk
}
}
return logs;
}
// Usage
// const logs = await getLogsInChunks(provider, '0x...', 19000000, 19010000);
// Expected output: "Fetched N logs from 19000000 to 19010000" etc.Troubleshooting Checklist for Ethereum RPC Timeouts
When you encounter an Ethereum RPC timeout, follow this checklist to isolate the cause and apply the right fix. This is a systematic method you can reproduce in your own environment.
- Check the error type: Is it a timeout, a 429, or a JSON-RPC error? Use the error code and message.
- Review your provider configuration: What are the timeout settings in ethers.js/viem? Are they too low?
- Identify the specific RPC method: Is it
eth_getLogs,eth_call,eth_sendRawTransaction, or adebug_method?
- Identify the specific RPC method: Is it
- For
eth_getLogs, narrow the block range and page by chunks. Test with a small range to confirm.
- For
- For
eth_call, check if the contract call is heavy (e.g., loops over large arrays). Consider caching or using a multicall with smaller batches.
- For
- For
eth_sendRawTransaction, ensure you are not retrying blindly. Use a nonce manager and check receipts.
- For
- Test with a different RPC provider to rule out network issues.
- Use
eth_subscribefor real-time data instead of polling.
- Use
- Implement exponential backoff with jitter for retries, but only for idempotent requests.
- Monitor your request volume and rate limits to avoid 429s, which can also cause timeouts.
Tradeoffs and Limitations
Increasing timeouts can mask underlying performance issues. A request that takes 60 seconds is often a sign of an inefficient query, not a slow network. Always optimize the query first, then adjust timeouts as a last resort.
Retry patterns can increase load on the RPC provider and may trigger rate limits. Use retries sparingly and with exponential backoff. For state-changing transactions, retries are dangerous; always check the transaction status before resubmitting.
Some public RPC endpoints disable debug_ and trace_ methods for security and performance reasons. If you need these, consider a dedicated node or a provider that supports them.
Batch requests can reduce the number of round trips, but not all providers support them. Check your provider's documentation.
- Long timeouts can hide inefficient queries.
- Retries increase load and may cause 429s.
- State-changing transactions require careful retry handling.
- Not all providers support batch requests or expensive methods.
Next Steps and Further Reading
Now that you understand Ethereum RPC timeouts, you can apply these fixes to your own applications. For a broader view of RPC troubleshooting, see our generic RPC timeout diagnosis and fixes. If you're building on Ethereum, explore the best Ethereum RPC API (RPC Assistant) to compare providers. For more about our service, check the API service and RPC pricing.
For deeper dives, refer to the ethereum.org JSON-RPC API docs and the Quicknode Ethereum Error Code Reference. These are authoritative sources for RPC behavior and error handling.
- Read the generic RPC timeout guide for cross-chain issues.
- Compare providers with the best Ethereum RPC API.
- Learn about our API service and RPC pricing.
- Explore the OnFinality Learn hub for more tutorials.
- Check the Ethereum network page for network-specific details.