# Execution reverted: what it means and how to fix it

> Execution reverted means the contract code stopped on purpose. See the two ways you meet it, how to read the reason, and how it looks in a trace.

Source: https://hashspan.dev/errors/execution-reverted

## Short answer

Execution reverted means the contract's code stopped on purpose, with a revert or a failed require, and undid its changes. If the gas is estimated first, you get the error and its reason before anything is sent. If the transaction was sent with a gas limit, it is mined with status reverted and the gas is paid. Read the reason, then fix the input or the state.

## What you will see

- `execution reverted`
- `execution reverted: Withdrawals are paused`
- `Execution reverted with reason: Withdrawals are paused.`

## Where does it stop?

Figure: A transaction's path from your code to the node, the mempool, a block and a receipt. The block that runs the transaction is marked in red with the error execution reverted: it has a hash and a receipt with status reverted.

The contract's own code decided to stop. That happens when the transaction runs, so a mined transaction has a hash and a receipt with status reverted. You can also meet the same revert earlier, when the gas is estimated.

## Two ways to meet the same revert

Figure: Two paths for the same revert. With no gas limit given, viem asks the node to estimate first: the estimate reverts with the reason Withdrawals are paused, nothing is sent, and the send span ends with an error. With a gas limit given, the transaction is sent, mined and reverted, and hashspan replays it with eth_call to read the reason; the gas is spent.

- **The estimate fails first.** With a local account and no gas limit, viem asks the node to estimate the gas. The revert happens there, and you get the reason before anything is sent: nothing is mined and no gas is spent.
- **The transaction is mined and reverts.** With a gas limit you set, or when the state changed after the estimate, the transaction is sent and mined, and the receipt says reverted. The gas is spent.

An address-only account, such as a wallet or a dev node that signs for you, does not go through viem's estimate: viem hands the transaction to the wallet or node, which decides about gas. In our Anvil run it was accepted and got a hash.

## Why does it happen?

- **A failed `require` or an explicit `revert`.** The contract refuses: it is paused, a limit is exceeded, an allowance or a balance is too low.
- **Wrong arguments or wrong state.** The call is valid, but not for the contract's state now.
- **The state changed since the estimate.** Another transaction got there first.

## How do I fix it?

1. **Read the reason.** viem prints it for an estimate. For a mined transaction, read it from the trace or decode it with the contract's ABI.
2. **Run the call before you send it.** A simulation returns the revert and its reason and costs nothing:

```ts

declare const account: `0x${string}`;
declare const vault: `0x${string}`;
const publicClient = createPublicClient({ chain: mainnet, transport: http() });
const abi = parseAbi(['function withdraw(uint256 amount)']);

// Run the call against the current state first: a revert comes back with its reason and costs nothing.
const { request } = await publicClient.simulateContract({ account, address: vault, abi, functionName: 'withdraw', args: [1n] });
```

3. **In an agent, simulate inside the tool** and return the reason to the model, so it can change the amount or stop, instead of sending a transaction that is bound to revert.

## What does it look like in a trace?

When the estimate fails, the `send` span ends with an error and no hash, like any refusal. Since hashspan 1.1.0 its `error.type` is `ExecutionRevertedError`, the name viem gives a revert. When the transaction is mined, the `confirm` span carries the reason:

<AttributeCard
span="confirm 31337"
status="bad"
rows={[
['blockchain.tx.status', 'reverted'],
['blockchain.tx.revert.reason', 'Withdrawals are paused'],
['blockchain.tx.gas.used', '21054'],
['blockchain.tx.fee', '42108000000000'],
['error.type', 'reverted'],
]}
caption="The mined revert of the run above. The reason is decoded by replaying the transaction as a call."
/>

The replay uses the state of the previous block, so a transaction that depended on another one in the same block can show no reason or a different one. The long version, with the trace of an agent whose withdrawal reverted, is in [your agent's trace stops at the tool call](https://hashspan.dev/blog/agent-trace-stops-at-the-tool-call), and a query for them is in [find reverted transactions](https://hashspan.dev/docs/recipes/find-reverted-transactions). A revert without any reason, with gas used equal to the limit, is [out of gas](https://hashspan.dev/errors/out-of-gas).

## FAQ

### Where is the revert reason?

In the data the contract returns when it reverts. For require(condition, "text") that is an Error(string). A mined transaction does not store it, so a receipt only says reverted. A node returns it for a call or an estimate, and hashspan replays the transaction as a call at the previous block to decode it. A custom error needs the contract's ABI to be decoded.

### Does a reverted transaction cost gas?

If it was mined, yes: in our run it used 21,054 gas. If the estimate caught the revert first, nothing was sent, so nothing was spent.

### Is it the same as out of gas?

No. Out of gas has no revert reason: the code used the whole limit. A revert has a reason when the contract gave one, and it usually uses part of the limit.
