What happened?
If a smart contract reverts but tries to build an error message which contains a nested error for example:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts@~4.9.0/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts@~4.9.0/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts@~4.9.0/access/Ownable.sol";
contract WillThrowString {
function aFunction() public pure {
require(false, "Error message");
}
}
contract WillThrowError {
error AnError(string message);
function aFunction() public pure {
revert AnError("I am an error");
}
}
contract WillThrowBytes {
error AnError(bytes message);
function aFunction() public pure {
revert AnError(hex'deadbeef');
}
}
contract Token is ERC20, ERC20Burnable, Ownable {
constructor() ERC20("Token", "TKN") {}
function mint(address to, uint256 amount) public onlyOwner {
if(amount == 1){
WillThrowString will = new WillThrowString();
try will.aFunction() {
_mint(to, 1000000); //if you reach this impossible statement you deserve 1 million tokens!
}
catch Error(string memory reason) {
revert(string.concat("[404]", "01c - caught error", reason));
}
catch(bytes memory reason){
revert(string.concat("[404]", "01d - caught bytes", string(reason)));
}
}
if(amount == 2){
WillThrowError will = new WillThrowError();
try will.aFunction() {
_mint(to, 2000000); //if you reach this impossible statement you deserve 2 million tokens!
}
catch Error(string memory reason) {
revert(string.concat("[404]", "01c - caught error", reason));
}
catch(bytes memory reason){
revert(string.concat("[404]", "01d - caught bytes", string(reason)));
}
}
if(amount == 3){
WillThrowBytes will = new WillThrowBytes();
try will.aFunction() {
_mint(to, 3000000); //if you reach this impossible statement you deserve 3 million tokens!
}
catch Error(string memory reason) {
revert(string.concat("[404]", "01c - caught error", reason));
}
catch(bytes memory reason){
revert(string.concat("[404]", "01d - caught bytes", string(reason)));
}
}
_mint(to, amount);
}
}
Cases 2 and 3 result in an error like the following:
[2026-02-27T12:37:44.884Z] ERROR SQL update failed: pq: invalid byte sequence for encoding "UTF8": 0x00 sql=[ UPDATE operations SET opstatus = $1, error = $2, updated = $3 WHERE (id = $4 AND namespace = $5) ] dbtx=Qirvs5Un ns=ff1 opupdater=opu_004 pid=....
The problem is caused because there is nested ABI contained in the string which results in null character in the error message that is passed to the DB update. For instance:
FF10111: Error from ethereum connector: FF23021: EVM reverted: [404]01d - caught bytes:���������������������������������� ��������������������������������ޭ������������������������������
Or in hex:
0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000007b5b3430345d303164202d20636175676874206 2797465733aac8ae000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000
The error is repeatedly retried forever and can, if enough reverts happen occupy all of the operation update workers.
What did you expect to happen?
The error message would ideally be decoded to properly interpret the nested structures but at the very least the DB update should sanitize the error string before calling update on the DB.
How can we reproduce it (as minimally and precisely as possible)?
Use the contract above to force a revert by minting quantity=2 or 3 tokens.
Anything else we need to know?
No response
OS version
Details
Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:26 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6041 arm64
What happened?
If a smart contract reverts but tries to build an error message which contains a nested error for example:
Cases 2 and 3 result in an error like the following:
The problem is caused because there is nested ABI contained in the string which results in null character in the error message that is passed to the DB update. For instance:
Or in hex:
The error is repeatedly retried forever and can, if enough reverts happen occupy all of the operation update workers.
What did you expect to happen?
The error message would ideally be decoded to properly interpret the nested structures but at the very least the DB update should sanitize the error string before calling update on the DB.
How can we reproduce it (as minimally and precisely as possible)?
Use the contract above to force a revert by minting quantity=2 or 3 tokens.
Anything else we need to know?
No response
OS version
Details
Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:26 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6041 arm64