A transaction that sits in "pending" isn't lost and isn't half-executed. It's waiting in the mempool because block builders don't find it worth including yet, or because it can't be included until an earlier transaction from your account is. Nothing on-chain has happened, so you can still replace it: speeding up and cancelling are both done by sending a new transaction with the same nonce and higher fees.
Why transactions get stuck
1. The max fee is below the current base fee. Since EIP-1559, every block has a base fee that rises when blocks are full and falls when they're not. A transaction is only eligible when its maxFeePerGas is at least the base fee. If you signed during a quiet period and demand then jumped, your transaction waits until the base fee comes back down, which may never happen.
2. The priority fee (tip) is too low. Even when eligible, builders order transactions by what they earn from them. A tip far below what others are paying can leave you waiting while busier blocks fill up.
3. An earlier nonce is stuck. Transactions from one account are processed strictly in nonce order. If nonce 41 is stuck, nonces 42, 43 and 44 all wait behind it, however high their fees. This is the most common cause when "everything" is stuck at once.
4. A nonce gap. If you sent nonce 43 but nonce 42 was never broadcast (it failed in your code, or was dropped), 43 sits in the node's "queued" pool forever.
5. It was dropped, but your wallet doesn't know. Nodes evict low-paying transactions when their mempool is full, and different nodes see different mempools. The wallet may show "pending" for a transaction that no node is holding any more.
Diagnose it
Look up the transaction on a block explorer or through your RPC, and compare the account's mined and pending nonces:
# Is the transaction known to this node, and what did it offer?
cast tx 0xYourTxHash --rpc-url $RPC_URL
# Current base fee, to compare with the transaction's maxFeePerGas
cast base-fee --rpc-url $RPC_URL
# Mined nonce vs. nonce including pending transactions
cast nonce $MY_ADDRESS --block latest --rpc-url $RPC_URL
cast nonce $MY_ADDRESS --block pending --rpc-url $RPC_URL
How to read that:
- The latest nonce is the nonce of the first unmined transaction. If that's not the transaction you're worried about, fix the earlier one first.
- If
maxFeePerGasis below the base fee, you must replace it or wait. - If
cast txfinds nothing and the explorer doesn't know the hash either, the transaction has been dropped. Its nonce is free again, and you can simply send a new transaction with it.
Speed it up
Send the same transaction (same to, data, value) with the same nonce and higher fees. Nodes only accept a replacement if it pays noticeably more than the original. Geth's default requires at least a 10% increase on both maxFeePerGas and maxPriorityFeePerGas, and some nodes and providers require more. Otherwise you'll get replacement transaction underpriced.
In MetaMask and most wallets, the Speed up button does exactly this. From the command line with Foundry:
cast send $CONTRACT "claim(uint256)" 7 \
--nonce 41 \
--gas-price 40gwei \
--priority-gas-price 3gwei \
--account deployer \
--rpc-url $RPC_URL
For EIP-1559 transactions, --gas-price sets maxFeePerGas and --priority-gas-price sets the tip. Choose a max fee comfortably above the current base fee, since you only pay the base fee plus the tip and the rest is not charged. --account uses an encrypted keystore created with cast wallet import, which is safer than pasting a private key into your shell history.
Cancel it
There's no cancel instruction in Ethereum. "Cancel" means replacing the transaction with one that does nothing: send 0 ETH to your own address with the same nonce and higher fees:
cast send $MY_ADDRESS \
--value 0 \
--nonce 41 \
--gas-price 40gwei \
--priority-gas-price 3gwei \
--account deployer \
--rpc-url $RPC_URL
This costs the base gas of a plain transfer (21,000 gas) at the new fee. When it's mined, nonce 41 is used up and the original can never execute. The wallet Cancel button does the same thing.
The same in ethers v6:
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const tx = await wallet.sendTransaction({
to: wallet.address,
value: 0n,
nonce: 41,
maxFeePerGas: ethers.parseUnits("40", "gwei"),
maxPriorityFeePerGas: ethers.parseUnits("3", "gwei"),
});
await tx.wait();
Things to know before you click
- It's a race. Replacing doesn't pull the original back. Whichever version gets included first wins, and the other is rejected as "nonce too low". Always check which hash was actually mined.
- Replace the oldest stuck nonce. Replacing nonce 44 does nothing while 41 is stuck.
- If you sent from several tools (wallet, script, another device), make sure they agree on the nonce. Wallets can also show stale nonces; MetaMask lets you set a custom nonce once enabled in its advanced settings.
- You can't cancel a mined transaction. If it's in a block, even a failed one, it's final.
- L2s behave differently. On rollups like Arbitrum, Optimism and Base, a central sequencer orders transactions and there usually isn't a long public mempool wait. A transaction that looks stuck there is more often a nonce problem, an RPC issue or a transaction that was rejected outright.
Avoid it next time
- Use the fee estimates from your provider (
provider.getFeeData()in ethers,estimateFeesPerGasin viem), and givemaxFeePerGasheadroom, often about twice the current base fee. You only pay what's actually needed. - Don't set a tiny tip to save a little. You'll often pay more to replace it.
- In backends, track your own nonces and monitor pending transactions, then automatically bump fees after a timeout.
Checklist
- Find the lowest pending nonce for your account; that's the one to fix.
- Compare the transaction's
maxFeePerGaswith the current base fee. - To speed up, resend with the same nonce and both fee fields raised by more than 10%.
- To cancel, send 0 ETH to yourself with the same nonce and higher fees.
- Confirm which hash was mined before doing anything else.
Get the weekly commit
New blockchain deep dives every week.