A revert reason is a string of text that a smart contract emits when it executes the revert opcode, terminating a transaction. Introduced with Ethereum's EIP-140 in 2017, it replaced generic, uninformative failures with specific error messages. When a transaction reverts, the reason is recorded in the transaction receipt and can be parsed by clients and block explorers, making it a fundamental tool for developers to understand why a call failed, such as insufficient funds or a failed precondition.
Revert Reason
What is a Revert Reason?
A revert reason is a human-readable error message returned by a smart contract when a transaction fails, providing crucial debugging information on-chain.
Technically, the revert reason is passed as an argument to the revert() statement in Solidity, like revert("InsufficientBalance"). In modern Solidity (0.8.4+), the error keyword allows for gas-efficient, typed revert reasons. These messages are encoded as ABI-encoded data within the revert payload. While the transaction's state changes are rolled back and gas is consumed up to the point of failure, the revert reason itself is permanently stored in the blockchain's event logs, incurring a minor gas cost for the data storage.
For developers, analyzing revert reasons is essential for debugging and building robust applications. Wallets and dApp interfaces use them to display user-friendly error messages. Common patterns include access control failures ("Ownable: caller is not the owner"), arithmetic overflows/underflows, and custom business logic violations. Tools like Ethers.js and web3.py provide methods to decode these reasons from transaction receipts, turning opaque failures into actionable insights.
How Revert Reasons Work
A technical breakdown of revert reasons, the human-readable error messages that provide crucial debugging information when a smart contract transaction fails on the Ethereum Virtual Machine (EVM).
A revert reason is a human-readable error message returned by a smart contract when a transaction fails due to a require(), revert(), or assert() statement. This mechanism, introduced in Ethereum's Byzantium hard fork via EIP-140, replaced generic failure codes with descriptive strings, allowing developers and users to understand why a transaction was reverted. The reason is encoded in the transaction's revert data and can be decoded by wallets, block explorers, and developer tools to display the specific failure condition, such as "Insufficient balance" or "Sale has ended."
When a contract executes a require(condition, "Error message"), the EVM checks the condition. If it evaluates to false, execution halts, all state changes are rolled back, and the provided string is packaged as the revert reason. This data is included in the transaction receipt under the revertReason or output data field. Importantly, while gas for executed opcodes is consumed, the remaining gas is refunded to the caller, making revert reasons a gas-efficient way to enforce conditions compared to older patterns like throw.
From a developer's perspective, using descriptive revert reasons is a best practice for security and auditability. It makes failed transactions debuggable without needing to inspect contract bytecode. For example, require(msg.sender == owner, "Unauthorized"); immediately clarifies the access control issue. Tools like Ethers.js and Web3.py have built-in methods to parse this data. However, developers must be mindful that longer strings increase gas costs for the failing transaction, as the reason data is stored on-chain in the transaction receipt.
For end-users and applications, revert reasons are surfaced through wallet interfaces (like MetaMask) and block explorers (like Etherscan), transforming a cryptic failure into an actionable insight. This transparency is critical for decentralized applications (dApps), where users need to understand if a failure was due to insufficient funds, a deadline, or a slippage tolerance. The ability to catch and display these reasons in a front-end application significantly improves the user experience and trust in the protocol's behavior.
Under the hood, the revert reason is an ABI-encoded call to a hypothetical function Error(string). The encoded data starts with the function selector for Error(string) (0x08c379a0), followed by the offset and length of the string data. This standardization allows any EVM-compatible client or library to decode the message consistently. It's a foundational piece of the EVM's error reporting schema, which has since been expanded with custom error types (EIP-838) for more gas-efficient and structured error handling.
Key Features and Characteristics
A revert reason is a human-readable error message included in a failed transaction to explain why execution was halted. It is a critical debugging tool for developers and users.
The `require()` Statement
The primary mechanism for generating revert reasons. It validates a condition and, if false, reverts the transaction with a specified message.
Example:
require(balance >= amount, "Insufficient funds for transfer");
- Gas Refund: Reverts with
require()orrevert()refund all remaining gas to the caller, unlike the olderthrowkeyword or failedassert().
The `revert()` Statement
An unconditional function that immediately halts execution and reverts state changes. It can accept a custom error string or a custom error type.
Example with string:
revert("Transaction expired");
Example with custom error (gas-efficient):
revert UnauthorizedCaller();
- Flexibility: Allows for more complex conditional logic than
require().
Custom Error Types
Introduced in Solidity v0.8.4, custom errors are a gas-efficient alternative to string error messages. They are defined with the error keyword.
Definition: error InsufficientBalance(uint256 available, uint256 required);
Usage: revert InsufficientBalance(balance, amount);
- Benefits: Significantly cheaper gas cost because the error signature and parameters are ABI-encoded, not a full string stored on-chain.
ABI-Encoded Data
The revert reason is returned to the caller as ABI-encoded data. For string messages, it's encoded as a function call to Error(string). For custom errors, it's encoded as a call to the error's signature.
- Off-Chain Decoding: Wallets and block explorers decode this data to display the human-readable message.
- Inspection: The raw data is visible in transaction receipts as the
revertReasonor within theoutputfield of a failed call.
Debugging & User Experience
Revert reasons transform cryptic transaction failures into actionable insights.
For Developers:
- Pinpoints the exact failing condition in a smart contract.
- Essential for testing and integration.
For Users:
- Wallets can display clear messages like "Insufficient liquidity" instead of a generic "transaction failed".
- Improves transparency and trust in dApp interactions.
Gas and Execution Context
Understanding gas implications is crucial.
- Gas Refund: All gas is refunded for a revert triggered by
require(),revert(), or a custom error. - No Refund for
assert(): Theassert()function consumes all remaining gas, used for checking internal invariants that should never fail. - Bubble Up: Reverts propagate up the call chain, undoing all state changes in the entire transaction.
Comparison: Revert Mechanisms
A comparison of the primary methods for reverting transactions and providing error information in Solidity smart contracts.
| Feature | require() | revert() | assert() | custom error |
|---|---|---|---|---|
Primary Use Case | Validate inputs & conditions | Complex conditional logic | Check for internal invariants | Gas-efficient custom logic |
Error String Support | ||||
Custom Error Type Support | ||||
Gas Refund on Revert | All remaining gas | All remaining gas | No gas refund | All remaining gas |
Opcode Used | 0xfd | 0xfd | 0xfe | 0xfd |
Typical Gas Cost (Revert) | ~21k gas + string cost | ~21k gas + string cost | Consumes all gas | ~21k gas |
Introduced In | Solidity 0.4.10 | Solidity 0.4.12 | Solidity 0.4.10 | Solidity 0.8.4 |
Recommended For | Input validation, simple guards | Complex branching logic | Internal state sanity checks | Production contracts, gas optimization |
Ecosystem Usage and Standards
A revert reason is a human-readable error message included in a failed transaction, providing critical context for debugging and user feedback. It is a key standard for improving developer and user experience across the EVM ecosystem.
The `require()` Statement
The primary Solidity function for implementing revert reasons is require(). It validates a condition and, if false, reverts the transaction with a specified error string.
- Syntax:
require(condition, "Descriptive error message"); - Example:
require(msg.value >= 1 ether, "Insufficient payment"); - Gas: The error string consumes gas, so it's often omitted in production for non-critical checks to save costs.
The `revert()` Statement
The revert() function unconditionally aborts execution and can be used with a custom error or a string reason. It's more gas-efficient than require() for complex conditional logic.
- Syntax with string:
revert("Operation failed"); - Syntax with custom error:
revert CustomError(arg1, arg2); - Use Case: Ideal for complex validation in the middle of a function or in
if/elseblocks whererequire()is less suitable.
Custom Error Types (EIP-838)
Introduced in Solidity 0.8.4, custom errors are a gas-efficient alternative to string reasons. They are defined with the error keyword and can take parameters.
- Definition:
error InsufficientBalance(uint256 available, uint256 required); - Usage:
revert InsufficientBalance(balance, amount); - Advantage: Significantly cheaper than string messages, as only the 4-byte selector and ABI-encoded parameters are stored on-chain.
EVM Opcode: REVERT
At the lowest level, revert reasons are implemented by the REVERT opcode (0xfd). This opcode halts execution, rolls back state changes, and can optionally return a memory segment containing the error data.
- Mechanism: The opcode takes two stack arguments: an offset and a size within memory where the error data (e.g., ABI-encoded string or custom error) is stored.
- Result: This data is returned to the caller in the transaction receipt's
outputfield for a call, or as part of the receipt for a transaction.
Transaction Receipt & Debugging
When a transaction reverts, the reason is captured in the transaction receipt, enabling off-chain tools and user interfaces to display what went wrong.
- Field: The
revertReasonor decoded error is typically parsed from the receipt'soutputorstatusdata. - Tooling: Wallets (MetaMask), block explorers (Etherscan), and developer frameworks (Hardhat, Foundry) automatically decode and display these messages.
- Critical for UX: This transforms a generic "transaction failed" into a specific, actionable error like "ERC20: transfer amount exceeds balance".
Gas Implications & Best Practices
Including revert reasons has a gas cost, leading to established optimization practices.
- String vs. Custom Error: A string reason can cost ~20k+ gas, while a custom error costs only ~100 gas for the selector.
- Production vs. Development: Use descriptive strings in development and testing. For production, consider:
- Using custom errors.
- Omitting the string in
require()for non-user-facing checks. - Providing error codes that map to off-chain explanations.
Security and Gas Considerations
A revert reason is a human-readable error message included in a failed transaction, providing critical debugging information and affecting gas costs.
Definition and Purpose
A revert reason is a string of text that explains why a smart contract transaction failed. It is emitted by the require(), revert(), or assert() statements in Solidity. This provides developers and users with immediate, on-chain feedback for debugging and understanding transaction failures, moving beyond opaque generic errors like "execution reverted."
Gas Cost Implications
Storing the revert reason string on-chain consumes gas. The cost scales with the length of the message. This creates a trade-off: detailed error messages aid debugging but increase the gas cost of a failed transaction. For this reason, production contracts often use short, coded error messages (e.g., "INSUFFICIENT_BALANCE") or custom error types introduced in Solidity 0.8.4, which are more gas-efficient.
Custom Errors (Gas-Efficient Alternative)
Introduced in Solidity 0.8.4, custom errors are defined with the error keyword (e.g., error InsufficientBalance(uint256 available, uint256 required)). They provide a more gas-efficient and type-safe alternative to string revert reasons. When a custom error is used with revert, only a 4-byte selector is stored on-chain, saving significant gas compared to storing full strings, especially for frequent failure conditions.
Security Best Practices
- Avoid Sensitive Data: Never include private keys, passwords, or internal state details in revert messages, as they are publicly visible on-chain.
- Use for Input Validation: Clearly state which invariant was violated (e.g.,
require(msg.value >= price, "PRICE_NOT_MET")). - Prevent Information Leakage: In access-controlled functions, use generic errors like "UNAUTHORIZED" to avoid revealing system architecture.
- Balance Clarity with Cost: Use coded messages or custom errors for common failures to optimize gas.
Example in Solidity
solidity// Using a string revert reason (more gas) require(balance >= amount, "Insufficient balance for transfer"); // Using a custom error (gas-optimized) error InsufficientBalance(uint256 available, uint256 required); if (balance < amount) { revert InsufficientBalance(balance, amount); }
The custom error provides structured data (the available and required amounts) without the gas overhead of a long string.
Related Concepts
- Revert: The opcode that halts execution and rolls back state changes.
- Require(): A Solidity function that checks a condition and reverts with an optional message.
- Assert(): Used for internal invariants; prior to Solidity 0.8.0, it consumed all gas on failure.
- Gas Refund: Failed transactions still consume gas for computation up to the point of revert, but any remaining gas is refunded to the caller.
- Error Handling: The broader pattern of managing failures in smart contract interactions.
Common Misconceptions
Revert reasons are a fundamental error-handling mechanism in Ethereum smart contracts, but their behavior and limitations are often misunderstood. This section clarifies how they work, what they cost, and why they aren't a silver bullet for debugging.
A revert reason is a human-readable error message that a smart contract can provide when it intentionally aborts execution using require(), revert(), or a custom error. It is a feature introduced in Ethereum's Constantinople hard fork (EIP-140) that replaced generic, uninformative reverts with descriptive strings or custom error signatures. When a transaction reverts, this message is recorded on-chain as part of the transaction receipt, allowing developers and users to understand why the operation failed, such as "Insufficient balance" or "Ownable: caller is not the owner".
Key Mechanism: The reason is encoded in the revert payload and can be decoded by clients like Etherscan or wallet interfaces. It's important to note that while the reason is stored, it does not consume gas for the transaction that triggered it, as all state changes are rolled back.
Technical Deep Dive
A revert reason is a mechanism in Ethereum and EVM-compatible blockchains that allows a failed transaction to return a human-readable explanation for its failure. This section explores its technical implementation, use cases, and impact on development and debugging.
A revert reason is a human-readable error message that a smart contract can emit when it intentionally aborts execution via the revert() statement. It provides critical debugging information by explaining why a transaction failed, such as "Insufficient balance" or "Unauthorized caller." This is a significant improvement over the older throw keyword or silent failures, which only consumed all gas without explanation. Revert reasons are encoded in the transaction receipt data and are a core feature of Solidity versions 0.4.22 and above, implemented through the require(), revert(), and assert() functions.
Example in Solidity:
solidityrequire(balance >= amount, "Insufficient balance for transfer"); revert("Emergency stop activated");
Frequently Asked Questions
A revert reason is a message returned by a smart contract when a transaction fails, providing critical debugging information. These FAQs cover its purpose, usage, and technical details.
A revert reason is a human-readable error message that a smart contract can emit when a transaction fails due to a require(), revert(), or assert() statement. It is a critical debugging tool that replaces generic failures with specific explanations, such as "Insufficient balance" or "Not the owner." Introduced with EIP-140 in 2017, revert reasons are encoded in the transaction's revert data and can be decoded by clients and block explorers to show why a call was unsuccessful. This provides developers and users with immediate, actionable feedback instead of a cryptic, generic error.
Get In Touch
today.
Our experts will offer a free quote and a 30min call to discuss your project.