🏠 Home
Beginner
01 — Introduction to Solidity 02 — Setting Up Your Environment 03 — Your First Smart Contract 04 — Data Types & Variables 05 — Functions & Visibility 06 — Control Flow 07 — Arrays & Mappings 08 — Structs & Enums
Intermediate
09 — Events & Logging 10 — Modifiers 11 — Inheritance 12 — Interfaces & Abstract Contracts 13 — Error Handling 14 — Ether & Wei 15 — Payable Functions 16 — msg.sender & msg.value 17 — Storage vs Memory vs Stack
Advanced
18 — Gas Optimization 19 — ERC-20 Tokens 20 — ERC-721 NFT Standard 21 — Contract Security 22 — Reentrancy Attacks 23 — Oracles & Chainlink 24 — Upgradeable Contracts 25 — Deploying to Mainnet
SolidityMaster / Lesson 13
Lesson 13 of 25

Error Handling

require, revert, assert — handle errors gracefully and save users gas.

Intermediate

require — Input Validation

require(condition, message) is the standard way to validate inputs and state. If the condition is false, execution reverts with the message and all gas (above a threshold) is returned to the caller.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract RequireDemo {
    uint256 public balance;
    address public owner;

    constructor() { owner = msg.sender; }

    function deposit(uint256 amount) public {
        require(amount > 0, "Amount must be positive");
        require(amount <= 1000 ether, "Too large");
        balance += amount;
    }

    function withdraw(uint256 amount) public {
        require(msg.sender == owner, "Not owner");
        require(amount <= balance, "Insufficient funds");
        balance -= amount;
    }
}

revert — Custom Errors (0.8.4+)

Custom errors are more gas-efficient than string messages. Define them at the top of your contract, then use revert CustomError().
Solidity
contract CustomErrors {
    // Define custom errors (gas efficient — no string storage)
    error NotOwner(address caller, address owner);
    error InsufficientBalance(uint256 requested, uint256 available);
    error ZeroAmount();

    address public owner;
    mapping(address => uint256) public balances;

    constructor() { owner = msg.sender; }

    function withdraw(uint256 amount) public {
        if (msg.sender != owner)
            revert NotOwner(msg.sender, owner);

        if (amount == 0)
            revert ZeroAmount();

        if (balances[msg.sender] < amount)
            revert InsufficientBalance(amount, balances[msg.sender]);

        balances[msg.sender] -= amount;
    }
}

assert — Invariant Checks

assert(condition) is used to check for internal bugs — conditions that should NEVER be false if the contract is correct. Unlike require, assert failures consume ALL remaining gas (indicating a critical bug).
Solidity
contract AssertDemo {
    uint256 public totalSupply;
    mapping(address => uint256) public balances;

    function mint(address to, uint256 amount) public {
        balances[to] += amount;
        totalSupply += amount;

        // This should always be true — if not, there's a critical bug
        assert(totalSupply >= balances[to]);
    }
}

try / catch — External Call Errors

Handle errors from external contract calls using try/catch.
Solidity
interface IExternal {
    function riskyOperation() external returns (uint256);
}

contract TryCatchDemo {
    event Success(uint256 result);
    event Failed(string reason);

    function callExternal(address target) public {
        try IExternal(target).riskyOperation() returns (uint256 result) {
            emit Success(result);
        } catch Error(string memory reason) {
            emit Failed(reason);       // revert with message
        } catch {
            emit Failed("Unknown error"); // panic or low-level revert
        }
    }
}