Skip to main content
Provider requests throw errors following the JSON-RPC 2.0 and EIP-1474 specifications.

Error Structure

interface JsonRpcErrorType {
  code: number;
  message: string;
  data?: unknown;
}
// Zig: parse JSON-RPC response and check for error field
const std = @import("std");

pub fn parseResponse(allocator: std.mem.Allocator, body: []const u8) !void {
    var parsed = try std.json.parseFromSlice(std.json.Value, allocator, body, .{});
    defer parsed.deinit();
    const obj = parsed.value;
    if (obj.get("error")) |err_val| {
        const code = err_val.object.get("code").?.integer;
        const message = err_val.object.get("message").?.string;
        std.debug.print("JSON-RPC error {d}: {s}\n", .{ code, message });
        return error.JsonRpcError;
    }
    // else: handle result
}

Standard JSON-RPC 2.0 Error Codes

Defined by JSON-RPC 2.0 specification:
CodeConstantDescription
-32700PARSE_ERRORInvalid JSON received by server
-32600INVALID_REQUESTJSON is not a valid request object
-32601METHOD_NOT_FOUNDMethod does not exist or is not available
-32602INVALID_PARAMSInvalid method parameter(s)
-32603INTERNAL_ERRORInternal JSON-RPC error

Ethereum-Specific Error Codes (EIP-1474)

Defined by EIP-1474 in the -32000 to -32099 range:
CodeConstantDescription
-32000INVALID_INPUTMissing or invalid parameters (commonly “execution reverted”)
-32001RESOURCE_NOT_FOUNDRequested resource not found (block, transaction, etc.)
-32002RESOURCE_UNAVAILABLERequested resource not available (node syncing, data not ready)
-32003TRANSACTION_REJECTEDTransaction creation failed
-32004METHOD_NOT_SUPPORTEDMethod exists but is not implemented
-32005LIMIT_EXCEEDEDRequest exceeds defined limit
-32006JSON_RPC_VERSION_NOT_SUPPORTEDJSON-RPC protocol version not supported

Using Error Codes

import {
  JsonRpcError,
  INVALID_INPUT,
  RESOURCE_NOT_FOUND,
  TRANSACTION_REJECTED,
} from '@tevm/voltaire/JsonRpcError';

// Create error with code and message
const error = JsonRpcError.from(INVALID_INPUT, 'execution reverted');

// Create error with additional data (e.g., revert reason)
const revertError = JsonRpcError.from(
  INVALID_INPUT,
  'execution reverted',
  '0x08c379a0...' // ABI-encoded revert reason
);

// Check error code
if (response.error?.code === INVALID_INPUT) {
  console.log('Contract execution failed');
}

Execution Reverted Errors

The -32000 (INVALID_INPUT) error code is most commonly used for contract execution failures:
import * as Rpc from '@tevm/voltaire/jsonrpc';
import { INVALID_INPUT } from '@tevm/voltaire/JsonRpcError';

try {
  const result = await provider.request(
    Rpc.Eth.CallRequest({
      to: contractAddress,
      data: encodedCall
    })
  );
} catch (error) {
  if (error.code === INVALID_INPUT) {
    // Contract reverted - data may contain revert reason
    console.error('Execution reverted:', error.data);
  }
}

## Error Handling Patterns

### Checking for Specific Errors

```zig
import * as Rpc from '@tevm/voltaire/jsonrpc';
import {
  INVALID_INPUT,
  METHOD_NOT_FOUND,
  RESOURCE_NOT_FOUND,
} from '@tevm/voltaire/JsonRpcError';

try {
  const block = await provider.request(
    Rpc.Eth.GetBlockByHashRequest(blockHash, false)
  );
} catch (error) {
  switch (error.code) {
    case INVALID_INPUT:
      console.error('Invalid block hash format');
      break;
    case RESOURCE_NOT_FOUND:
      console.error('Block not found');
      break;
    case METHOD_NOT_FOUND:
      console.error('Method not supported by this provider');
      break;
    default:
      console.error('Unexpected error:', error.message);
  }
}
// Zig pattern: if response contains {"error":{"code":-32000,"message":"execution reverted","data":"0x..."}}
// read error.code and error.data to surface revert reason

Retry Strategies

Smart Retry with Error Code Filtering

Only retry on transient errors (not permanent failures):
import {
  METHOD_NOT_FOUND,
  INVALID_PARAMS,
} from '@tevm/voltaire/JsonRpcError';

async function fetchWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3
): Promise<T> {
  const NON_RETRYABLE_ERRORS = new Set([
    METHOD_NOT_FOUND,    // Method will never exist
    INVALID_PARAMS,      // Parameters are wrong
  ]);

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      // Don't retry if error is non-retryable or last attempt
      if (NON_RETRYABLE_ERRORS.has(error.code) || i === maxRetries - 1) {
        throw error;
      }

      // Exponential backoff: 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
    }
  }

  throw new Error('Max retries exceeded');
}

Rate Limit Handling

import { LIMIT_EXCEEDED } from '@tevm/voltaire/JsonRpcError';

async function callWithRateLimit<T>(
  fn: () => Promise<T>
): Promise<T> {
  while (true) {
    try {
      return await fn();
    } catch (error) {
      // If rate limited, wait and retry
      if (error.code === LIMIT_EXCEEDED) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        continue;
      }
      throw error;
    }
  }
}

Complete Error Reference

See @tevm/voltaire/JsonRpcError for:
  • All error code constants
  • JsonRpcError.from() constructor
  • ERROR_MESSAGES lookup table