# Transaction stuck pending: causes and fix

> A transaction with a hash but no receipt is waiting in the mempool. Find out if it is a nonce gap or a fee below the base fee, and how to unstick it.

Source: https://hashspan.dev/errors/transaction-stuck-pending

## Short answer

A stuck transaction was accepted and has a hash, but no block includes it. The two usual causes are a nonce gap, where an earlier nonce from the same account is missing, and a max fee below the current base fee. Compare your transaction's nonce with the account's nonce: send the missing nonce, or replace the stuck transaction with higher fees.

## What you will see

- `WaitForTransactionReceiptTimeoutError`
- `Timed out while waiting for transaction with hash`

## Where does it stop?

Figure: A transaction's path from your code to the node, the mempool, a block and a receipt. The mempool is marked: the transaction has a hash but no receipt yet, and the block and receipt steps are dashed.

The node accepted the transaction and returned a hash. It is in the mempool, but no block has included it. Nothing failed yet, so there is no error from the node, only a wait that doesn't end.

## Why is it stuck?

There are two common reasons. Check them in this order.

**1. A nonce gap.** An account's transactions run in nonce order. If nonce 0 was never sent, nonce 1 can't run. geth calls it "queued", [scheduled for future execution](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool), as opposed to "pending" for the next blocks.

Figure: Nonce 0 was never sent. A transaction with nonce 1 is queued with a hash, and the wait for it times out after 5 seconds; txpool_status shows pending 0 and queued 1. After nonce 0 is sent, block 1 mines both, and both succeed.

**2. The fee is below the base fee.** [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) only lets a block include a transaction whose `maxFeePerGas` is at least that block's base fee. If the base fee climbed after you sent, your transaction waits for it to come down. A cap that is already below the base fee when you send can be refused outright: that is [max fee per gas less than block base fee](https://hashspan.dev/errors/max-fee-per-gas-less-than-block-base-fee).

## How do I find which one it is?

1. **Compare nonces.** Read the account's nonce with `blockTag: 'latest'` and look up your transaction's nonce. If yours is higher, the nonces in between are missing.
2. **Compare fees.** Look up the latest block's `baseFeePerGas`. If your `maxFeePerGas` is below it, the fee is the problem.
3. **Check where the node keeps it.** On a node that exposes `txpool_status`, a gap shows as queued, not pending.

```ts

declare const client: PublicClient;
declare const hash: Hash; // the stuck transaction

const tx = await client.getTransaction({ hash });
const latest = await client.getTransactionCount({ address: tx.from, blockTag: 'latest' });
const block = await client.getBlock();

if (tx.nonce > latest) console.log(`nonce gap: ${latest} to ${tx.nonce - 1} are missing`);
if (tx.maxFeePerGas !== undefined && block.baseFeePerGas !== null && tx.maxFeePerGas < block.baseFeePerGas)
  console.log('max fee is below the current base fee');
```

## How do I fix it?

- **Nonce gap:** send the missing nonce. In our run, both transactions were mined in the next block.
- **Fee too low:** replace it. Send the same nonce with both fee fields at least 10% higher, [geth's default bump](https://hashspan.dev/errors/replacement-transaction-underpriced). To give up on it, send 0 to yourself at the same nonce instead.
- **In code:** set a `timeout` on the wait and decide what to do when it fires. viem's default is 180 seconds.

If you replace it, the old hash shows as [dropped and replaced](https://hashspan.dev/errors/transaction-dropped-and-replaced) on a block explorer.

## Why does the gap happen?

- **A send failed after the nonce was taken.** Code that counts nonces itself moved on while the node never got the transaction.
- **Two senders share one key.** Each keeps its own count, and one skips ahead.
- **A transaction was dropped.** A node evicted it, and the next nonce now waits for it.

## What does it look like in a trace?

The `send` span ends normally with a hash. The `confirm` span is where the wait shows:

<AttributeCard
span="confirm 31337 · the stuck transaction"
status="bad"
rows={[
['error.type', 'timeout'],
['blockchain.tx.status', '(not set)'],
]}
caption="From the run above: the wait gave up after 5 seconds. When nonce 0 was sent, a later wait recorded a second confirm span for the same hash with blockchain.tx.status success in block 1."
/>

A long gap between `send` and `confirm`, or a `timeout`, is the trace's sign of a stuck transaction. The steps from hash to receipt are in the [transaction lifecycle](https://hashspan.dev/learn/tx-lifecycle).

## FAQ

### How long can a transaction stay pending?

It depends on the node. geth keeps a transaction that cannot run yet, such as one behind a nonce gap, for 3 hours by default (txpool.lifetime). A transaction that can run stays until it is mined, replaced or pushed out of a full pool.

### Why does my wallet show the transaction as pending when the nonce looks right?

Check the fee. Under EIP-1559 a block can only include a transaction whose max fee per gas is at least the block's base fee. If the base fee rose after you sent it, it waits until the base fee drops or you replace it with a higher fee.

### Why didn't eth_getTransactionCount with pending show my queued transaction?

In our run on Anvil, the pending count stayed at 0 while the nonce-1 transaction sat in the pool as queued. There, the count pointed at the missing nonce, which is the one to send. Other nodes may count differently, so compare with the latest count too.

### Should I cancel or speed up a stuck transaction?

If it waits behind a nonce gap, neither: send the missing nonce and both go through. If its fee is too low, speed it up by sending the same nonce with both fee fields at least 10% higher, or cancel it with a 0-value transaction to yourself at the same nonce.
