Check existing issues
Viem Version
2.47.18
Current Behavior
The fallback transport only falls through to the next RPC on transport-level errors (network failures, HTTP 5xx). It does not retry on execution-level errors like NonceTooLowError, IntrinsicGasTooLowError, or other JSON-RPC errors returned with HTTP 200.
This means if an RPC returns a valid JSON-RPC error response (e.g. {"error":{"code":-32000,"message":"nonce too low"}}), viem treats the transport as "working" and never tries the next RPC.
Additional issue: cross-RPC state mismatch
Even if the fallback did retry on execution errors, there's a deeper problem. A single sendTransaction call internally makes multiple RPC requests:
- eth_getTransactionCount (nonce)
- eth_estimateGas
- eth_sendRawTransaction
Since the fallback transport handles each request() independently, if the first RPC fails mid-operation (e.g. succeeds on eth_getTransactionCount but fails on eth_sendRawTransaction), the next request may go to a different RPC. This RPC might have a different view of the chain state, causing the nonce (already baked into the signed transaction) to be invalid.
There is no way to ensure a single RPC is used for the entire lifecycle of a multi-step client operation like sendTransaction or writeContract.
This issue is very visible when there was a hard fork and not all RPC providers updated their nodes, or simpy one is stuck and provides old nonce of the address.
Expected Behavior
The fallback mechanism should have another flag that if triggered - would treat not getting a hash of a TX back as an error, so the retry mechanism on another RPC would kick in.
Steps To Reproduce
You can run 2 client versions, for example on gnosis: https://github.com/NethermindEth/nethermind/releases/tag/1.36.2 and v1.36.1.
Anything else?
We had to abandon the fallback transport and implement our own retry logic at the client level — creating separate walletClient/publicClient instances per RPC and retrying the entire operation on the next client.
Check existing issues
Viem Version
2.47.18
Current Behavior
The fallback transport only falls through to the next RPC on transport-level errors (network failures, HTTP 5xx). It does not retry on execution-level errors like NonceTooLowError, IntrinsicGasTooLowError, or other JSON-RPC errors returned with HTTP 200.
This means if an RPC returns a valid JSON-RPC error response (e.g. {"error":{"code":-32000,"message":"nonce too low"}}), viem treats the transport as "working" and never tries the next RPC.
Additional issue: cross-RPC state mismatch
Even if the fallback did retry on execution errors, there's a deeper problem. A single sendTransaction call internally makes multiple RPC requests:
Since the fallback transport handles each request() independently, if the first RPC fails mid-operation (e.g. succeeds on eth_getTransactionCount but fails on eth_sendRawTransaction), the next request may go to a different RPC. This RPC might have a different view of the chain state, causing the nonce (already baked into the signed transaction) to be invalid.
There is no way to ensure a single RPC is used for the entire lifecycle of a multi-step client operation like sendTransaction or writeContract.
This issue is very visible when there was a hard fork and not all RPC providers updated their nodes, or simpy one is stuck and provides old nonce of the address.
Expected Behavior
The fallback mechanism should have another flag that if triggered - would treat not getting a hash of a TX back as an error, so the retry mechanism on another RPC would kick in.
Steps To Reproduce
You can run 2 client versions, for example on gnosis: https://github.com/NethermindEth/nethermind/releases/tag/1.36.2 and v1.36.1.
Anything else?
We had to abandon the fallback transport and implement our own retry logic at the client level — creating separate walletClient/publicClient instances per RPC and retrying the entire operation on the next client.