Skip to content
LogoLogo

Errors

guillotine.CallError (src/errors.zig) is the error set used by the internal execution paths. vm.call() does not return it — it converts failures into a CallResult with success = false. You see CallError from inner_call, inner_create, accessAddress, preWarmTransaction, step, and the async entry points.

pub const CallError = error{
    // execution
    InvalidJump,
    InvalidJumpDestination,
    MissingJumpDestMetadata,
    InvalidOpcode,
    InvalidBytecode,
    InvalidPush,
    TruncatedPush,
    OutOfGas,
    OutOfBounds,
    StackUnderflow,
    StackOverflow,
    RevertExecution,
    ExecutionTimeout,
 
    // calls and creates
    CallDepthExceeded,
    ContractNotFound,
    ContractCollision,
    InsufficientBalance,
    StaticCallViolation,
    WriteProtection,
    CreateInitCodeSizeLimit,
    CreateContractSizeLimit,
    InitcodeTooLarge,
    BytecodeTooLarge,
 
    // state and memory
    MemoryError,
    StorageError,
    AccountNotFound,
    PrecompileError,
    OutOfMemory,
    AllocationError,
    NoSpaceLeft,
 
    // async
    NeedAsyncData,
};

The ones worth understanding

ErrorSpec counterpartWhen
OutOfGasOutOfGasErrorany charge that exceeds remaining gas
RevertExecutionRevertthe REVERT opcode; unspent gas is returned
InvalidOpcodeInvalidOpcodeundefined byte, or an opcode not yet active in this fork
InvalidJump / InvalidJumpDestinationInvalidJumpDestErrorJUMP/JUMPI to a non-JUMPDEST
StackUnderflow / StackOverflowStackUnderflowError / StackOverflowErrorbounded by config.stack_size
WriteProtectionWriteInStaticContextstate-changing op inside a STATICCALL
StaticCallViolationdittovalue-bearing call inside a static context
CallDepthExceededdepth limitbeyond config.max_call_depth; surfaces as CALL pushing 0
InsufficientBalancevalue transfer exceeding the sender's balance
ContractCollisionAddressCollisionCREATE/CREATE2 to an occupied address
CreateInitCodeSizeLimit / InitcodeTooLargeEIP-3860initcode over 49 152 bytes
CreateContractSizeLimit / BytecodeTooLargeEIP-170returned code over 24 576 bytes
NeedAsyncDatanot a failure: the EVM is asking for state and can be resumed
ExecutionTimeoutthe loop_quota safety valve tripped

NeedAsyncData is the one to special-case. It means "call executeUntilYieldOrComplete again once you have supplied the value", not "this execution failed".

Mapping failures back to spec behaviour

Most exceptional halts consume all remaining gas and return no data, which is why CallResult collapses them to success = false, gas_left = 0. REVERT is the exception: gas is preserved and the payload comes back in output. When you need the specific reason inside Zig, call inner_call and inspect the error instead of going through call().