Where does it stop?
The node compares your nonce with the account’s next one before it accepts the transaction. If yours is higher, a node that refuses gaps sends it back at once: no hash, no block, no gas spent.
What does the node check?
A nonce counts the transactions an account has sent, and each one can be used once, in order. geth names this error for a nonce “higher than the next one expected based on the local chain” (core/error.go).
What a node does with a gap differs. In our run, the same send had two outcomes:
A node that queues the gap gives no error now. The transaction waits instead, and you see it as a transaction stuck pending.
Why does it happen?
- The nonce is set by hand. Code passes a
nonceit computed itself, and the count is ahead of the chain. - The chain was reset. A local node restarted from an empty chain, but a script or wallet kept its old count.
- Earlier sends failed. Code that counts nonces moved on, but the transactions for the skipped nonces never reached the node.
How do I fix it?
- Read the next nonce from the node. With a
pendingblock tag, the count includes transactions already waiting in the pool for the next blocks. - Leave
nonceout. viem then asks the node for it before it sends. In our run, the same account sent fine once the nonce was left out. - Or send the missing nonces first. If the skipped transactions still matter, send them in order. The later one can follow.
- In an agent, keep one nonce source per account, such as viem’s
nonceManager, so parallel tool calls don’t count on their own. In our run, parallel sends that shared a count failed the other way, as nonce too low. That run shows what a manager fixes and what it doesn’t.
import type { Address, PublicClient } from 'viem';
declare const client: PublicClient;declare const address: Address;declare const myNonce: number; // the nonce your code was about to use
const next = await client.getTransactionCount({ address, blockTag: 'pending' });if (myNonce > next) console.log(`nonce too high: the node expects ${next}, you have ${myNonce}`);What does it look like in a trace?
Like every refusal by the node, it is a send span that ends with an error and no hash:
- error.type
- NonceTooHighError
- blockchain.tx.nonce
- 5
- exception.message
- Nonce provided for the transaction (5) is higher than the next one expected.
- blockchain.tx.hash
- not set
The span records the nonce you sent, so compare it with the account’s count at that time. A gap the node queued shows differently: the send succeeds and the confirm span times out.