Skip to main content
A node can suspend the entire workflow instead of completing — to wait on a human approval, an external callback, or any out-of-band decision. The run stops cleanly with its state preserved, and you resume it later from exactly where it paused. Already-completed nodes are not re-executed, so resumes are idempotent and side-effect-free. This is the engine-level primitive behind human-in-the-loop approval gates.

Pausing a run

Return workflow.Pause(payload) from a node’s Execute. The payload is an opaque, host-defined bag describing what the run is waiting on — it’s surfaced to the caller so they can act on it.
When a node pauses, the run returns without an error. The engine:
  • marks the node NodeStatusPaused,
  • stamps Input.Pause with a PauseState{NodeID, Payload},
  • records any sibling nodes that completed in the same wave, and
  • stops the walk.

Durability: persist the paused run

The entire Input is the run’s durable memory — every field carries JSON tags. When a run pauses, serialize out and store it; restore it when the decision arrives.

Resuming

Stage the decision with SetResume, then run the same compiled graph again with the restored input:
SetResume(nodeID, decision) stores the decision for that node and clears Input.Pause, so the run is no longer considered suspended. On the resume walk:
  • The engine re-walks from the roots, but any node already marked NodeStatusCompleted is short-circuited — its cached output port is replayed to follow its edges instead of re-executing it. Completed nodes never run twice.
  • The previously paused node runs again; this time in.Resume(nodeID) returns the staged decision, so it proceeds instead of pausing.
The paused node decides what to do with the decision — typically emitting a port ("approved" / "rejected") that routes the rest of the graph.

Detecting a pause from an error

If you call a node’s Execute directly, or wrap it, use IsPauseErr to distinguish a pause from a real failure:
The built-in InProcessRuntime already does this for you — it translates a node’s PauseError into a paused run rather than a failed one.

End-to-end shape