Your wallet says 'Failed'. You paid gas anyway. And there is no obvious explanation of what went wrong. It is one of the most common and most frustrating moments in crypto, and almost every failed transaction on Ethereum and other EVM chains comes down to a small handful of causes.
This guide explains each one in plain English: what it means, how to confirm it, and how to fix it.
The short answer
A failed transaction means the network ran your transaction but the smart contract rejected it, so your action was undone while you still pay for the gas used. The cause is almost always one of: not enough gas, a require() check that failed, a custom contract error, a Solidity panic such as arithmetic overflow, or a slippage or deadline guard on a swap.
First, why you still paid gas
Gas pays for computation, not for success. When you submit a transaction, validators execute it step by step, and that work costs gas whether the transaction succeeds or reverts. If the contract hits a condition it will not allow, it reverts: every state change is rolled back as if it never happened, but the gas already spent on the computation up to that point is gone. This is why a failed swap can still cost you a few dollars.
The six reasons a transaction fails
1. Out of gas
The transaction ran out of the gas limit partway through execution. The gas limit is the maximum amount of computation you authorised, and if the contract needs more than that, it stops and reverts.
The most common confusion
Out of gas is about the gas LIMIT you set for the transaction, not your ETH balance. You can have plenty of ETH and still run out of gas if the limit was set too low. The fix is to raise the gas limit in your wallet's advanced settings and resubmit. Wallets estimate this automatically, but complex contract calls sometimes need a manual bump.
2. Reverted with a reason string
Smart contracts guard themselves with require() checks, and many include a human-readable message: require(balance >= amount, 'insufficient balance'). When that check fails, the message is your answer. Common reason strings include 'insufficient allowance', 'slippage', 'transfer amount exceeds balance', and 'expired'. A good block explorer shows this string directly on the failed transaction.
3. Custom errors
Modern contracts increasingly use custom errors instead of reason strings, for example error SlippageTooHigh() or error DeadlinePassed(). They are cheaper on gas, but they show up on a block explorer as a raw hex selector such as 0x8199f5f3 unless the explorer knows the contract's ABI. To translate a custom error into plain English, you need the ABI of the contract that threw it, or a tool that decodes it for you.
4. Solidity panics
Panics are automatic errors the Solidity compiler inserts to catch programming mistakes. Each has a numeric code:
- 0x11 - an arithmetic overflow or underflow (a number went above its maximum or below zero)
- 0x12 - a division by zero
- 0x32 - an array index that is out of bounds
- 0x31 - calling .pop() on an empty array
- 0x01 - an assert() check failed, which usually signals a bug in the contract
- 0x41 - the contract ran out of memory
A panic almost always means the contract was in a state it did not expect. If your inputs look correct, this can indicate a bug in the contract itself rather than anything you did wrong.
5. Slippage or deadline exceeded
On DEX swaps, this is the single most common failure. Slippage means the price moved beyond the tolerance you set between the moment you submitted and the moment the transaction was mined; the contract refuses to fill the trade at a worse price to protect you. A deadline error means the transaction sat in the mempool longer than the time window the app allowed. The fix is usually to raise your slippage tolerance slightly, or simply retry when the network is less congested.
6. Insufficient balance or allowance
Two different things get bundled together here. Insufficient balance means you do not hold enough of the token you are trying to move, or not enough native coin to cover gas. Insufficient allowance means you never granted the contract permission to spend your ERC-20 tokens: token transfers require a separate approval transaction first, which is why many swaps are two steps.
Pending forever is not the same as failed
A transaction that is stuck as pending has not failed at all. It usually means the gas price was set too low for current network conditions, or there is a nonce gap because an earlier transaction from your wallet is still unconfirmed. Nothing reverted; the network simply has not picked it up yet. The fix is to speed it up with a higher fee, cancel it with a zero-value replacement at the same nonce, or reset your wallet's nonce if transactions are queued behind a stuck one.
How to find the real reason yourself
- Open the transaction on the block explorer for its chain (Etherscan, Basescan, BscScan, Arbiscan, and so on).
- Look for the red 'Fail' status and any reason string shown directly beneath it.
- Check the gas used against the gas limit: if it used close to 100 percent of the limit, it ran out of gas.
- If you see a custom error hex selector with no label, the explorer does not have the contract's ABI, and you will need the ABI to decode it.
- For a deep diagnosis, tools can replay the transaction at the block it was mined to recover the exact revert reason, even when the explorer cannot.
Skip the detective work
Open the free TxID Support transaction checker at txid.support/check, pick the protocol you were using, and paste your wallet address. It finds the transaction, replays it, and tells you the cause in plain English, including out-of-gas, reason strings, custom errors, and panics, with no wallet connection required. Protocols embed the same engine on their own site so their users get the answer instantly instead of opening a support ticket.
What about Solana?
Solana transactions fail for similar reasons but with different mechanics. There is no gas limit to run out of, but a transaction can fail on insufficient lamports to cover fees, a program instruction that returns an error, a slippage guard on a swap, or an account constraint that was not satisfied. The underlying idea is the same as on EVM chains: a program was asked to do something it would not allow, so it rejected the instruction and the transaction did not go through.
The vast majority of failed transactions are one of the categories above. Once you know which one you are looking at, the fix is usually quick: raise a limit, grant an approval, widen slippage, or retry. The hard part has always been translating the raw on-chain data into that plain-English category, which is exactly the gap a good diagnostic tool closes.