Skip to content
Runner · Reference

Result and error

The outcome and error objects of a tick.

Derived from packages/runtime-core/src/types.ts

Every tick produces an outcome. The runtime hands it to your plugin as one of two objects: ExecutionResult for onExecuted, and the narrower ErrorContext for onError. Both are read-only echoes of what already happened. Mutating them changes nothing — the tx is already submitted (or already failed).

ExecutionResult

onExecuted receives this for every outcome.

type ExecutionOutcome = "submitted" | "noop" | "failed";

interface ExecutionResult {
  outcome: ExecutionOutcome;
  actions: Action[];
  dryRun?: boolean;
  digest?: string;
  effectsStatus?: "success" | "failure";
  events?: ExecutionEvent[];
  gasUsed?: bigint;
  error?: string;
}
  • outcome: the verdict. "submitted" = the tx landed and effects succeeded. "noop" = every action was a noop (nothing to submit). "failed" = a client error (RPC, signing) or an on-chain Move abort.
  • actions: an echo of the [Action[]](/reference/actions) your decide() returned, kept for correlation. If auto-fund prepended a pmDeposit, it's in here.
  • dryRun: true when the tick was a devInspect simulation. In practice you won't see it set inside the hook — the runtime skips both hooks when dryRun === true, so sims never touch the same state backend production uses.
  • digest: the Sui tx digest. Set only when a real tx reached the chain. Absent on dry-runs and client errors.
  • effectsStatus: "success" or "failure", set when the tx (real or simulated) returned effects.
  • events: the [ExecutionEvent[]](#executionevent) the tx emitted (or simulated). Empty on failure.
  • gasUsed: total gas in MIST (computation + storage − rebate). An estimate when dryRun.
  • error: the error message. Set when outcome === "failed".

ExecutionEvent

interface ExecutionEvent {
  type: string;        // full type ID, e.g. "0xabc::vault::VaultMintBinary"
  parsedJson: unknown;
}

Match on the type string, then cast parsedJson. Use type.endsWith("::vault::VaultMintBinary") rather than a hardcoded package prefix, since the package ID is part of the full type.

ErrorContext

onError receives this, and only on failed. It runs after onExecuted, never instead of it. The point is a dedicated error path without branching on result.outcome.

interface ErrorContext {
  actions: Action[];
  phase: "on-chain" | "client";
  digest?: string;
  error: string;
  dryRun?: boolean;
}
  • phase: where it broke. "on-chain" = the tx was submitted but Move aborted, so you have a digest. "client" = RPC or signing failed before the tx ever reached the chain.
  • digest: set when phase === "on-chain".
  • error: the description. Move abort code names (EPositionTooLarge and friends) leak through here, so you can error.includes(...) to react to a specific revert.
  • actions: the same echo as in ExecutionResult — correlate which trade failed.
  • dryRun: like above, you won't see it set in the hook; the runtime skips onError on simulations.
phase is the first branch. "client" is transient: retry next tick. "on-chain" means a real revert you can act on, such as banning a degenerate strike.

From here, the lifecycle hooks topic shows the canonical patterns for both objects.