Solidity Custom Errors vs Require Strings: A Migration Checklist
Custom errors replace require strings with typed revert data in Solidity 0.8.4+, but require tooling support to decode.
04 Feb 2026, 00:34 UTC

The problem: opaque, expensive revert strings
When a Solidity transaction reverts, the caller needs to know why. Many contracts still use require with a plain-text message:
require(msg.sender == owner, "Only the owner can call this");
That string lives in the contract bytecode. Every revert path that uses it repeats the text, which increases deployment size and makes revert data harder for off-chain systems to interpret. A string also cannot carry structured values, such as the balance available versus the amount requested.
If you are compiling with Solidity 0.8.4 or newer, custom errors are the supported alternative. They let you define a typed error once and revert with parameters, producing a four-byte error selector instead of repeating long strings.
Declare and revert a custom error
Custom errors were introduced in Solidity 0.8.4. You declare them at contract level, outside functions:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Vault {
error InsufficientFunds(uint256 available, uint256 required);
mapping(address => uint256) public balance;
function withdraw(uint256 amount) external {
if (balance[msg.sender] < amount) {
revert InsufficientFunds(balance[msg.sender], amount);
}
balance[msg.sender] -= amount;
}
}
The error declaration names the error and lists its parameters. The revert statement supplies the actual values. The compiler emits an error selector derived from the signature InsufficientFunds(uint256,uint256) and ABI-encodes the arguments.
Worked example: compile and inspect
Save the contract as Vault.sol. From your project root, check the compiler version:
solc --version
Required permissions: read access to Vault.sol and write access to the output directory. No special system privileges are needed.
Expected check: the reported version must be 0.8.4 or newer. If it is older, solc rejects the error keyword.
Compile the contract:
solc --abi --bin --optimize -o build/ Vault.sol
Risk: do not assume the bytecode is smaller without measuring it in your own build. Custom errors usually reduce repeated string data, but the exact savings depend on your contract and optimizer settings.
To verify runtime behavior, send a withdrawal transaction from an account with insufficient funds using a local node such as Anvil, or Hardhat or Foundry. Inspect the transaction receipt. With the ABI available, tooling can decode the selector into InsufficientFunds and show the available and required values. Without the ABI, users may see only the raw error selector or a generic revert.
Trade-offs and limitations
- Compiler version: Custom errors require Solidity 0.8.4 or later. Older compiler versions and some legacy toolchains do not support the
errorkeyword. - Off-chain readability: A wallet or block explorer shows a readable message only if it has the contract ABI or error definition. Otherwise the caller sees a raw selector.
- ABI stability: Adding custom errors changes the contract's public ABI. Frontends, monitoring systems, and off-chain decoders must be updated to recognize the new error types.
- Mixed patterns: Combining
requirestrings and custom errors in one project can create inconsistent error handling. Pick one convention per contract or project.
Practical closing: check your toolchain first
Before adopting custom errors, run solc --version in your project. If the result is 0.8.4 or newer, use custom errors for new revert conditions, especially when structured parameters help the caller debug a failure. For existing contracts, weigh the bytecode savings against the cost of updating the ABI and any off-chain systems that parse revert data.
Verify on a testnet that your block explorer or wallet displays the decoded error before relying on custom errors in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.