Queueing a run
run() behaves like the host Run action. It resolves when submission finishes,
not when backend execution completes. false means another queue call was
already in flight and this call was folded into it.
Run part of the graph with explicit output nodes:
nodes
array is rejected rather than interpreted as “run everything.”
Queue lifecycle
Before submission
beforeSerialize, a
frontend resolver, or a supplier when those express the intent without graph
mutation.
After submission
onAfterRun means the submission attempt finished. It is not an execution-
complete event. Accepted submissions include prompt IDs and backend node counts;
rejected reports how many submissions the backend refused.
Validation rejection
Guarding a run
Use a guard when the decision may be asynchronous and must be made before the prompt is built:false cancels the attempt. A guard that
throws is treated as allowing the run. All guards share a short host timeout,
after which the run proceeds so one extension cannot make ComfyUI permanently
unrunnable. Do not place an indefinitely blocking dialog behind a guard.
onBeforeRun observes and prepares. guard can delay and cancel. Do not use one
as an approximation of the other.
Queue state and interruption
pending() includes the currently executing run. interrupt() stops that run;
it does not clear the remainder of the queue.
The API also exposes the host’s user-facing queue settings:
disableAutoQueue() before a self-interrupting conditional workflow so the
automatic runner does not immediately submit it again.
Observing backend execution
The root API resolves backend execution IDs, including nested subgraph paths:executingNode() is undefined between nodes and runs. Use
executionNode(id) instead of parsing nested execution IDs or looking up the
visible graph by the final numeric segment.
For results, register behavior on the node definition:
ExecutionResult.raw preserves custom output keys from the pack’s own backend.
PreviewFrame.url is an object URL revoked when the next frame arrives; copy or
consume it before retaining a preview beyond that lifetime.
Frontend-only nodes
Frontend nodes remain ordinary editor entities but do not execute on the backend. Define one withexecution: 'frontend', or mark a backend-defined type
with NodeDefBuilder.setExecution('frontend', resolver?).
A resolver answers what each of its own outputs means:
OutputResolution:
ResolveView:
selfgives the resolver’s ID, type, properties, groups, mode, color, own inputs and outputs, and widget values;nodesOfType(type)returns other frozen views in the same graph scope;self.input(nameOrIndex)creates the only reference a resolver may forward.
input.resolvedSource() and
resolvedSupplies() report a pending resolver as unresolved.
InputSlotHandle.resolvedSource() exposes the same final result for editor
behavior without changing topology.
Suppliers and broadcast behavior
A supplier is the supply-side counterpart to a resolver. It answers which unconnected inputs elsewhere in the same graph this node offers to feed:- one of the supplier’s own outputs;
- a literal;
- whatever feeds one of the supplier’s own inputs (
forwardInput).
unconnectedInputs() exposes matching data needed by real broadcast packs:
slot name, translated label, type, widget-input status, owner title/mode/color,
groups, and frozen owner properties.
When multiple suppliers claim one input, higher priority wins. Exact priority
ties feed nothing instead of making execution depend on graph order.
Resolution runs independently in each graph scope. It never crosses a subgraph
boundary.
Inspecting winning supplies
GraphScopeHandle offers the
same read for root or subgraph definitions.
Prompt-time widget serialization
Frontend resolution changes topology. A widget’sbeforeSerialize changes one
value for one destination: