Transaction stuck pending: causes and fix

Waiting in the pool. It has a hash but no receipt yet: it waits until it can be mined, is replaced or is dropped.

3 min read· Updated

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

Messages printed for it:

  • WaitForTransactionReceiptTimeoutError
  • Timed out while waiting for transaction with hash
On this page

Where does it stop?

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.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, as opposed to “pending” for the next blocks.

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.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 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.

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.
import type { Hash, PublicClient } from 'viem';
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. 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 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:

confirm 31337 · the stuck transactionerror
error.type
timeout
blockchain.tx.status
(not set)
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.

How this page was checked. Reproduced on a local Anvil chain (31337, Anvil 1.8.3) with viem 2.57.2 and @hashspan/viem 1.1.0 on 2026-10-18: a transfer sent with nonce 1 while nonce 0 was unused got a hash, sat in the pool as queued, and its wait timed out after 5 seconds; sending nonce 0 had both mined in block 1. The base fee rule is from EIP-1559 and geth's queue lifetime from its command-line options.

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.

See it in your own trace

Type to search the docs, Learn topics and the blog.