VM Execution Result#
Result Kinds#
Return
Represents successful execution of a sub-VM
VMError
Represents a VM produced error, such as non-zero exit code or exceeding resource limits.
It uses predefined string error codes.
Fatal VM Error
Every VM error is either fatal or not; both draw their code from the same set. A non-fatal error of a sub-VM is returned to its caller as Sub-VM Result Encoding. A fatal one is not catchable: the caller terminates with the same VM error, and propagation continues until the topmost VM boundary
Nested transport encodes a fatal VM error with result code 4. At the
topmost publication boundary, the executor MUST downgrade it to an ordinary
VMError with the same payload before producing the reported
result. Result code 4 is therefore forbidden in a top-level reported
result. Both its Execution Hash and its
Sub-VM Result Hash use VMError as the result kind
VM Error Code Format#
vm-error-code := public-code [ " # " detail ]
public-code— a sequence ofsnake_casecomponents separated by single spaces, drawn from the predefined codes. It never contains#.detail— optional free-form UTF-8 diagnostic. When present, it is separated from the public code by#surrounded by a single space on each side.
The full string, detail included, is covered by the Execution Hash, so it must be reproducible octet for octet by every implementation running the same execution. A detail MUST therefore be composed only of:
octets the execution itself produced or consumed — a guest-supplied string, a decoded field of the calldata or of a runner description;
values fixed by the WASM module or by this specification — a function or memory index, a limit, a constant from Constants;
literal text chosen by the code that raises the error.
Anything the host or the runtime environment supplies MUST NOT appear, even indirectly. In particular: filesystem paths, operating-system error strings or numbers, host addresses and pointer values, elapsed times, locale-dependent or platform-width-dependent formatting, thread or process identifiers, and implementation build or version strings.
Beyond that constraint the detail’s content carries no compatibility promise — see VM Error Code Compatibility.
Consumers MUST compare and match VM error codes only by the public code (the
part before the first `` # ). A consumer matching a known code ``P MUST
treat a code as matching iff its public code equals P or extends P
with further space-separated components.
UserError
Represents a user-produced error in utf-8 format.
Effects of a Non-Returning Run
Only a Return carries effects. A topmost run that ends in
UserError or VMError reports no
storage_deltas and no emissions, whatever it wrote or emitted before
failing, and its Execution Hash covers those empty fields.
InternalError#
Not a Result Kinds but the absence of one: the executor could not run the contract to a verdict at all, because of a Host communication failure or Module unavailability.
Internal errors are not visible by the contracts and are reported only to the Host, which will most likely vote timeout if it encounters one
Non-Deterministic Block Result Encoding#
Return: Arbitrary structure in Calldata Encoding
UserError: utf-8 string
VMError: utf-8 string
These three are the only codes a leader-proposed non-deterministic block result
may carry; validators treat every other byte as a malformed leader result. A
fatal VM error computed by a leader’s non-deterministic child propagates to its
caller and MUST NOT be included in LeaderPublicData
Leader Output Format#
LeaderPublicData uses Calldata Encoding with the shape
{"nd_outs": bytes[]}. The array contains every non-deterministic block result
in execution order; each element uses Sub-VM Result Encoding. Empty
bytes and all other representations are malformed
Contract Result Encoding#
Return#
Arbitrary structure in Calldata Encoding
UserError and VMError#
The error value (UserError value or VMError error code string) is reported
alongside two fingerprint fields, each Calldata Encoding encoded:
backtrace — the WASM call stack captured at the failure point:
[
{
"module_name": "<module_name>",
"func": "<number: function_index>"
}
]
wasm_store_hashes — per-module memory fingerprint:
{
"<module_name>": {
"memories": [
"<bytes: 32_byte_blake3_hash>"
]
}
}
For sake of preventing skipping execution for error results, validators are obligated to calculate the VM fingerprint on error.
Both fields are serialized using Calldata Encoding to be deterministic, and have the following structure:
backtraceframes are ordered from most recent to oldest one (most likely,_start)Function index is an index of function in WASM module
Memories are ordered by their index in WASM module
Memories are hashed using BLAKE3 hash function, which is cryptographically secure and provides acceptable performance
Execution Hash#
Every run produces an execution hash: a SHA3-256 digest over a Calldata Encoding encoding of the consensus-visible result, as a map with the following keys (in this order):
backtracedata— the contract result valuedata_fees_consumeddata_fees_remaining— map from each fee-bucket name to its remainingU256balanceemissions— emitted messages and events, in emission orderkind— the Result Kinds result codestorage_deltassubvm_hashes— see Sub-VM Result Hashwasm_store_hashes
Two runs that agree on the deterministic result produce the same execution hash, so consensus can compare a single 32-byte value instead of the full result.
A fatal VM error is committed with VMError as kind. Fatality controls
propagation and is not a distinct consensus-visible outcome
emissions covers the whole content of every emitted message and event, not
just its metered cost: two emissions can carry different calldata or different
event topics for the same fee, so a fee-only commitment would let nodes agree on
the hash while committing divergent side effects
Sub-VM Result Hash#
A deterministic run accumulates the result of each deterministic sub-VM call
into a rolling SHA3-256 accumulator. The finalized accumulator is the
subvm_hashes field of the parent’s Execution Hash.
For each deterministic sub-VM, its small hash is folded into the accumulator. The small hash is a SHA3-256 digest over a Calldata Encoding encoded map:
kind— the exact string"Return","UserError"or"VMError". Fatality is not an outcome of its own, only a statement about who may catch it, so a fatal result hashes as"VMError"result— forReturn/UserErrorthe result value in Calldata Encoding; forVMErrorthe error code stringsubvm_hashes— that sub-VM’s own finalized accumulator, making the hash recursive over the whole deterministic call treewasm_store_hashes
Non-deterministic sub-calls do not contribute. A run with no deterministic sub-calls finalizes its accumulator to a fixed digest, so that error and edge results hash uniformly.
The small hash of a sub-VM the host delegated to another executor (see Contract Major Version) is computed the same way, from the values that executor reports. Where the callee ran is not an input: a call that a host routes to another executor MUST fold the same value it would have folded had the callee run in-process.
Post-Execution Result Validation#
A consumer in Sync Mode or Validator Mode
consumes one leader-proposed result per non-deterministic block it runs. If the
leader supplied more results than the run consumed, the surplus blocks were
never reached: the run’s result is replaced by
leader_fault nondet_output extra, whose
parameter is the first 6 characters of the GVM32 (Base32) encoding of the
sha3_256 digest of the complete [result_code][data]
sub-VM result buffer the run would
otherwise have returned — the whole wire buffer as emitted, not a decoded
payload or alternate representation. No non-deterministic disagreement is caused
by this.
This check also applies when startup fails before any non-deterministic block, including when initial fees cannot be paid. An existing fatal result is preserved.