In this article, we’ll get a closer look at four main mechanisms for error handling in Solidity: functions assert
, require
, revert
, and another approach based on exceptions.

These mechanisms will help us tremendously in achieving stable and secure smart contracts, so this is the right moment to introduce them and lay the ground for a more thorough analysis.
It’s part of our long-standing tradition to make this (and other) articles a faithful companion, or a supplement to the official Solidity documentation.

assert()
We’d commonly use the assert(...)
function to test if a certain expression evaluates to an expected value. The function takes a boolean expression as an argument, and if the expression evaluates to false, the transaction will throw an exception and revert all changes made in the transaction.

π‘ Note: We should refrain from using the assert(...)
function in the production source code; it’s meant to be used mainly for testing and debugging.
Let’s take a look at a simple and familiar example of using the assert(...)
function:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; contract Divider { function divide(uint numerator, uint denominator) public pure returns (uint) { assert(denominator != 0); uint result = numerator / denominator; return result; } }
In the example above, the assert(...)
function is used to check that the denominator is not 0 before performing the division. If the denominator is 0, the transaction will throw an exception and revert any changes made by the transaction. This way, our smart contract is safe in terms of keeping the transaction in a consistent state.
require()
The require(...)
function is similar to the assert(...)
function, but contrary to the assert(...)
function, it’s recommended for use in production code. Just like the assert(...)
function, it takes a boolean expression as an argument and reverts the transaction if the expression evaluates to false.

Let’s take a look at an example of using require(...)
function:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; contract RequireExample { mapping(address => uint) balanceOf; function transfer(address _to, uint _value) public { // Ensures that the caller has sufficient funds. require(balanceOf[msg.sender] >= _value); // Transfers the funds. balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; } }
In this example, we use the require(...)
function to ensure that the caller of the transfer(...)
function has sufficient funds before transferring the specified amount. If the caller does not have sufficient funds, the transaction will throw an exception and revert any changes made by the transaction.
revert()
We’d use the revert(...)
function to explicitly revert the changes made by a transaction and throw an exception. It is often used in conjunction with the require(...)
function to revert the changes made by a transaction if a certain condition is not met.

Let’s take a look at an example of using revert(...)
:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; // This will report a warning contract RevertExample{ address private owner; function setOwner(address _newOwner) public { // Ensures that the caller is the current owner. require(msg.sender == owner); // Sets the new owner owner = _newOwner; }

Exceptions
Exceptions are a great mechanism available to us for error handling with external function calls and contract creation calls. They are similar to exceptions in other programming languages, but they work a bit differently in the context of a blockchain.

In Solidity, exceptions are thrown using the throw
keyword, and they can be caught using the try/catch
statements.
π‘ Recommended: Solidity Control Structures
Exceptions in many programming languages have the property of βbubbling upβ automatically until they are caught in a try/catch
statement. The exceptions to this rule are the send(...)
function and the low-level functions call(...)
, delegatecall(...)
and staticcall(...)
. These functions just return false
as their first return value in case of an exception, instead of βbubbling upβ.
β‘ Warning: The low-level functions call(...)
, delegatecall(...)
and staticcall(...)
return true
as their first return value if the account called is non-existent. We have discussed this behavior in earlier articles since it is a part of the design of the EVM. To circumvent it, we must check for the account’s existence before using the low-level functions.
π‘ Recommended: DelegateCall or Storage Collision Attack on Smart Contracts
Here is an example of using exceptions in a smart contract:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0 <0.9.0; contract Divider { function divide(uint numerator, uint denominator) public pure returns (uint) { uint result = numerator / denominator; return result; } } contract Operation { Divider divider = new Divider(); event Log(string message); function callOperation(uint numerator, uint denominator) public returns (uint result) { try divider.divide(numerator, denominator) returns (uint r) { return r; } catch { emit Log("External call threw an error."); } } }
In this example, the divide(...)
function throws an exception if the denominator is 0. The callOperation(...)
function calls the divide(...)
function and catches any exceptions that may be thrown. An exception is handled in the catch block by emitting the error message.

It is important to note that exceptions are expensive to use in Solidity because they require the use of gas to throw and catch the exception.
π‘ Recommended: Ethereum Gas – How It Works
With that in mind, we should use them sparingly and only when absolutely necessary.
Conclusion
In this article, we learned about four different mechanisms for handling errors in Solidity.
First, we got introduced to the assert(...)
function and explained which are its use cases, as well as which are not.
Second, we went through the require(...)
function and learned about its use cases.
Third, we got acquainted with the revert(...)
function and showed how it can be used to handle errors.
Fourth, we got familiar with exception handling and learned how they’re used for error handling with external function calls and contract creation calls.
What’s Next?
This tutorial is part of our extended Solidity documentation with videos and more accessible examples and explanations. You can navigate the series here (all links open in a new tab):

I’m an experienced computer science engineer and technology enthusiast dedicated to understanding how the world works and using my knowledge and ability to advance it. I’m focused on becoming an expert in Solidity and crypto technology, with a passion for coding, learning, and contributing to the Finxter mission of increasing the collective intelligence of humanity.