Hello guys, here is another blog on Solidity. Check out my previous blog on Value types in Solidity. In this blog, we will see how to do error handling in Solidity. Solidity uses state-reverting exceptions to handle errors. Such an exception undoes all changes made to the state in the current call and all its sub-calls and reports the error to the caller. When exceptions happen in a sub-call, they are rethrown automatically unless they are caught in a try/catch
statement.
Exceptions can contain error data that goes back to the caller in the form of error instances. The built-in errors Error(string)
and Panic(uint256)
are used by the special functions. Error
is used for “regular” error conditions while Panic
is used for errors that should not be present in bug-free code.

Panic and Error
The convenience functions assert
and require
can be used to check for conditions and throw an exception if the condition is not met.
The assert
function creates an error of type Panic(uint256)
. We should only use assert
to test for internal errors, and to check invariants. Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix. Language analysis tools can evaluate your contract to identify the conditions and function calls that will cause a Panic.
The require
function either creates an error without any data or an error of type Error(string)
. We use require
function to ensure valid conditions that we cannot detect until execution time. This includes conditions on inputs or return values from calls to external contracts.
An Error(string)
exception or an exception without data is generated by the compiler in the following situations:
- Calling
require(x)
wherex
evaluates tofalse
. - When you use
revert()
orrevert("description")
. - If you perform an external function call targeting a contract that contains no code.
- If your contract receives Ether via a public function without
payable
modifier (including the constructor and the fallback function). - When your contract receives Ether via a public getter function.
revert
statement
A direct revert can be triggered using the revert
statement and the revert
function.
The revert
statement takes a custom error as a direct argument without parentheses:
revert CustomError(arg1, arg2);
For backwards-compatibility reasons, there is also the revert()
function, which uses parentheses and accepts a string:
revert(); revert(“description”);
The error data goes back to the caller and we can catch it there. Using revert()
causes a revert without any error data while revert("description")
will create an Error(string)
error.
The following example shows how to use an error string and a custom error instance together with revert
and the equivalent require
:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract VendingMachine {
address owner;
error Unauthorized();
function buy(uint amount) public payable {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
// Alternative way to do it:
require(
amount <= msg.value / 2 ether,
"Not enough Ether provided."
);
// Perform the purchase.
}
function withdraw() public {
if (msg.sender != owner)
revert Unauthorized();
payable(msg.sender).transfer(address(this).balance);
}
}
try/catch
statement
We can use a try/catch statement to catch a failure in an external call. Here is an example.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
interface DataFeed { function getData(address token) external returns (uint value); }
contract FeedConsumer {
DataFeed feed;
uint errorCount;
function rate(address token) public returns (uint value, bool success) {
// Permanently disable the mechanism if there are
// more than 10 errors.
require(errorCount < 10);
try feed.getData(token) returns (uint v) {
return (v, true);
} catch Error(string memory /*reason*/) {
// This is executed in case
// revert was called inside getData
// and a reason string was provided.
errorCount++;
return (0, false);
} catch Panic(uint /*errorCode*/) {
// This is executed in case of a panic,
// i.e. a serious error like division by zero
// or overflow. The error code can be used
// to determine the kind of error.
errorCount++;
return (0, false);
} catch (bytes memory /*lowLevelData*/) {
// This is executed in case revert() was used.
errorCount++;
return (0, false);
}
}
}
The try
keyword has to be followed by an expression representing an external function call or a contract creation (new ContractName()
). For example, if it is a complex expression that also involves internal function calls, only a revert happens inside the external call itself. If the end of the success block is reached, execution continues after the catch
blocks.
This was all about error handling in Solidity. Stay tuned for more such blogs.
If you want to read more content like this? Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter: https://bit.ly/2Vdlld7.





