# Replacement transaction underpriced: cause and fix

> A second transaction with the same nonce must pay more than the first. See why the node refuses it, how much more, and how to speed up or cancel.

Source: https://hashspan.dev/errors/replacement-transaction-underpriced

## Short answer

A node keeps one pending transaction per sender and nonce. You sent a second one with the same nonce, and it did not pay enough more than the first, so the node refuses to replace it. Raise both fee fields by at least 10%, which is geth's default, or wait for the first transaction.

## What you will see

- `replacement transaction underpriced`
- `Transaction creation failed.`

## 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 replacement transaction underpriced, and the later steps are dashed: no hash and no receipt.

The node refuses the second transaction at its check, so nothing new reaches the mempool. The first transaction is still waiting, untouched.

## Why does a replacement need to pay more?

A node keeps one transaction for each sender and nonce. A new one with the same nonce can take its place, but only if it pays enough more.

Figure: Transaction A with nonce 2 waits with a max fee of 2 gwei and a tip of 1 gwei. Transaction B with the same nonce and the same fees is refused with replacement transaction underpriced. With fees 9% or 10% higher it is accepted on Anvil. geth's default is 10% and Anvil accepted 9%.

The rule is the client's. geth's default is a bump of 10% on [both fee fields](https://geth.ethereum.org/docs/fundamentals/command-line-options) (`txpool.pricebump`), and a replacement has to clear the threshold on the fee cap and on the tip.

## Why does it happen?

- **A retry sends the same fees again.** A timeout handler resends the transaction unchanged while the first one is still waiting.
- **A speed-up raises only one fee field.** `maxFeePerGas` goes up and `maxPriorityFeePerGas` stays, or the other way round.
- **The bump is smaller than the node wants.** You added 5% where the node asks for 10%.

## How do I fix it?

1. **Wait if you can.** If the fees were fine, the first transaction will be mined and a resend is not needed.
2. **To speed it up, send the same transaction again with both fee fields at least 10% higher.** The rule is the node's, so if 10% is refused, check what yours requires.
3. **To cancel it, send 0 to yourself with the same nonce and a higher fee:**

```ts

declare const account: `0x${string}`;
declare const nonce: number; // the nonce of the transaction that is still waiting
const client = createWalletClient({ account, chain: mainnet, transport: http() });

// Same nonce, both fee fields at least 10% higher than the waiting transaction's (2 and 1 gwei here).
await client.sendTransaction({
  to: account, // sending 0 to yourself cancels the first transaction instead of repeating it
  value: 0n,
  nonce,
  maxFeePerGas: parseGwei('2.4'),
  maxPriorityFeePerGas: parseGwei('1.2'),
});
```

If a wait for the first transaction had already read it, viem's `waitForTransactionReceipt` notices the replacement and resolves with the receipt of the transaction that took its place; if the replacement came first, the wait can run into its timeout instead ([why](https://hashspan.dev/errors/transaction-dropped-and-replaced#why-is-my-wait-stuck-after-a-replacement)). On a block explorer the first one then shows as [dropped and replaced](https://hashspan.dev/errors/transaction-dropped-and-replaced).

## What does it look like in a trace?

The refused attempt is a `send` span that ends with an error and no hash, like every refusal by the node. viem has no name for this one, so `error.type` is `TransactionRejectedRpcError` (see [nonce too low](https://hashspan.dev/errors/nonce-too-low#what-does-it-look-like-in-a-trace) for how to see the node's wording). An accepted replacement looks different. In the run above, with the cancel sent 500 ms after the wait started, the cancel replaced the waiting transaction and hashspan recorded both:

<AttributeCard
span="confirm 31337 · the original transaction"
rows={[
['blockchain.tx.status', 'replaced'],
['blockchain.tx.replacement.hash', 'the hash of the cancel transaction'],
]}
caption="From the run with the cancel sent 500 ms after the wait started. The replacing transaction's own confirm span has blockchain.tx.status success. Sent at once, the original's span ended as a timeout instead."
/>

The original span ends as `replaced`, with the hash of the transaction that took its place, and the replacement has its own `confirm` span with the outcome. How the receipt is attributed is in the [transaction lifecycle](https://hashspan.dev/learn/tx-lifecycle).

## FAQ

### How much more must a replacement pay?

It depends on the client. geth requires both the fee cap and the tip to be at least the txpool.pricebump percentage higher, and the default is 10. In our run Anvil accepted a 9% bump and refused an unchanged fee. If a bump of 10% is refused, check what your node requires.

### Why does raising only maxFeePerGas not work?

In geth the replacement must clear the threshold on both the fee cap (maxFeePerGas) and the tip (maxPriorityFeePerGas), so raising one of them is not enough.

### How do I cancel a pending transaction?

Send a transaction with the same nonce, no value, to your own address, and fees at least the bump higher. In our run on Anvil it replaced the waiting transaction. When the wait for the original had already read it (the cancel sent 500 ms later), the wait resolved with the cancel's receipt; when the cancel went out at once, the wait timed out.
