# Nonce too low in Ethereum: cause and fix

> Nonce too low means the account already used that nonce. See where the node refuses it, why retries and parallel workers cause it, and how to fix it.

Source: https://hashspan.dev/errors/nonce-too-low

## Short answer

Every account has a nonce: a counter of the transactions it has sent, and each value can be used once. "Nonce too low" means you sent a transaction with a nonce the account has already used, so the node refuses it before it reaches a block. Let the client choose the next nonce instead of setting it yourself.

## What you will see

- `nonce too low`
- `Nonce provided for the transaction is lower than the current nonce of the account.`

## Where does it stop?

Figure: A transaction's path from your code to the node, the mempool, a block and a receipt. The node's check is marked in red with the error nonce too low, and the later steps are dashed: no hash and no receipt.

The node checks every transaction before it puts it in the mempool, and the nonce is one of the checks. When it fails, the node answers at once with an error. There is no transaction hash and nothing reaches a block.

## What is a nonce?

A nonce is a counter on the account: [the number of transactions it has sent](https://ethereum.org/developers/docs/accounts). Only one transaction with a given nonce can run for an account, and that is what stops a signed transaction from being replayed.

So the next nonce is always the number of transactions the account has sent so far. Anything lower was used already, and the node says nonce too low.

Figure: An account whose transaction with nonce 0 is mined, so its next free nonce is 1. A second send with nonce 0 is refused with nonce too low, and no hash is named.

## Why does it happen?

Three causes cover most cases:

- **You set the nonce yourself and reused one.** A retry copies the nonce of the first attempt, and the first attempt was mined.
- **Two senders share one account.** Two workers, or two agent tool calls, each keep their own count. Sooner or later both send the same number.
- **A retry after a timeout.** The first attempt may have reached the chain even though no answer came back. If it was mined, sending the nonce again gives this error. If it is still waiting, you get [replacement transaction underpriced](https://hashspan.dev/errors/replacement-transaction-underpriced) instead.

## How do I fix it?

1. **Do not pass `nonce` unless you must.** With a local account (a private key), viem asks the node for the account's transaction count at the `pending` block and uses that. With an address-only account, such as a wallet or a dev node that signs for you, the wallet or node picks the nonce.
2. **Give the account a nonce manager when sends can overlap.** With a local account, viem can keep the counter in your process, so concurrent sends get consecutive nonces:

```ts

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`, { nonceManager });
const client = createWalletClient({ account, chain: mainnet, transport: http() });

// Concurrent sends from this process get consecutive nonces.
await Promise.all([
  client.sendTransaction({ to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', value: 1n }),
  client.sendTransaction({ to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', value: 2n }),
]);
```

3. **Before you retry after a timeout, find out what happened.** Look up the first attempt by its hash, or read the account's count again, and only send again if it did not land. A nonce manager covers one process: if several processes send from the same account, give each its own account or send through one place.

## What does it look like in a trace?

With hashspan, the refused send is a `send` span that ends with an error and has no transaction hash, since the node never named one:

<AttributeCard
span="send 31337"
status="bad"
rows={[
['error.type', 'NonceTooLowError'],
['exception.message', 'Nonce provided for the transaction is lower than the current nonce of the account.'],
['blockchain.tx.hash', 'not set'],
]}
caption="The refused second send of the run above. exception.message is on the span's exception event and appears only with errorMessages set to sanitized."
/>

By default a refused send records the error type, and since hashspan 1.1.0 that is the name viem gives the refusal: `NonceTooLowError`, `FeeCapTooLowError`, `InsufficientFundsError` or `IntrinsicGasTooLowError`. Where viem has no name for it, as with a replacement refusal, it is `TransactionRejectedRpcError`. Before 1.1.0 every one of them was recorded as `TransactionExecutionError`, the wrapper viem puts around them. The `exception` event still carries that wrapper as `exception.type`. To read the node's wording as well, turn on `errorMessages: 'sanitized'` (see the [core options](https://hashspan.dev/docs/adapters/core)). More on how a failed span is marked: [span status](https://hashspan.dev/learn/otel-span-status).

## FAQ

### What is a nonce in Ethereum?

A counter on an account: the number of transactions it has sent. Only one transaction with a given nonce can run for an account, which also stops a signed transaction from being replayed (ethereum.org, Accounts).

### Is "already known" the same error as "nonce too low"?

No. In geth, already known means the exact same transaction is already in the node's pool, so it is already waiting and sending it again is harmless. viem still files that node message under its NonceTooLowError, which is why you may see the nonce error for it.

### Why does an agent hit this more often than a person?

A person sends one transaction at a time from a wallet. An agent can start several tool calls at once, and each one may read or guess the nonce on its own. Two sends from one account then race for the same number.
