Previous Module
Wallet Connect Demo

⚠️ Error Handling: Timeouts & Reverts

Learn how to debug RPC failures and retry strategies

Communicate with blockchain nodes programmatically

⚠️ RPC Error Handling

RPC calls fail constantly. Network issues, invalid transactions, rate limits, node errors—production dApps must handle dozens of error types gracefully. Poor error handling = bad UX. A user sees "Error -32000" and leaves forever. A good dApp shows: "Insufficient ETH for gas. You need 0.01 ETH more." Let's master common RPC errors and how to handle them properly.

🎮 Interactive: Error Scenario Explorer

Select an error scenario to see the error code, message, root cause, solution, and real-world example. Learn how to detect and fix each error type in your dApps.

Error Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "insufficient funds for gas * price + value"
  }
}
🔍 Root Cause

Transaction costs more than wallet balance. Common when sending max ETH without leaving gas.

✅ Solution

Check balance with eth_getBalance. Reserve ~0.01 ETH for gas. Calculate: (gasLimit × gasPrice) + value < balance.

💡 Real Example

User tries to send 1 ETH but only has 1 ETH (no gas left). Solution: Send 0.99 ETH instead.

🛡️ Error Handling Best Practices

1️⃣
Retry with Exponential Backoff

Network errors (-32603, -32005) are often temporary. Retry 3-5 times with delays: 1s, 2s, 4s, 8s. Don't hammer the RPC provider—you'll get rate-limited. Code: await sleep(2 ** retryCount * 1000)

2️⃣
Validate Before Sending

Use eth_call to simulate transactions before eth_sendRawTransaction. If simulation reverts, show user why ("Insufficient allowance") instead of wasting their gas on a failing transaction.

3️⃣
Show User-Friendly Messages

Parse error codes and translate to plain English. Error -32000 "insufficient funds" → "You need 0.05 more ETH for this transaction". Use error.data for detailed revert reasons from contracts.

4️⃣
Implement Fallback Providers

If Infura is down, switch to Alchemy. If Alchemy fails, try Ankr. Use ethers.js FallbackProvider to automatically retry across multiple RPC endpoints. Never rely on single provider.

💻 Code Example: Robust Error Handling

async function sendTransaction(tx, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      // Simulate first to catch reverts
      await provider.call(tx);
      
      // Send transaction
      const txResponse = await wallet.sendTransaction(tx);
      return await txResponse.wait();
      
    } catch (error) {
      // Parse error
      if (error.code === -32000) {
        if (error.message.includes('insufficient funds')) {
          throw new Error('You need more ETH for gas fees');
        }
        if (error.message.includes('nonce too low')) {
          // Refresh nonce and retry
          tx.nonce = await wallet.getTransactionCount();
          continue;
        }
      }
      
      // Network error - retry with backoff
      if (error.code === -32603 && i < maxRetries - 1) {
        await sleep(2 ** i * 1000);
        continue;
      }
      
      // Unrecoverable error
      throw error;
    }
  }
}

This pattern handles nonce issues, validates before sending, retries network errors, and translates error codes to user-friendly messages.

💡 Key Insight

90% of user complaints about "broken dApps" are actually poor error handling. The blockchain didn't fail—your code showed a cryptic error instead of explaining the problem. Professional dApps catch every error type, validate before sending, retry network failures, and show clear messages like "Transaction will fail: Insufficient token allowance. Click to approve." Good error handling is what separates toy projects from production dApps.

← Common Methods