What Happens When You Press Queue

When a person presses Queue in ComfyUI, the system does not immediately “run the picture.” It receives a serialized workflow, decides whether that graph is admissible, selects the result paths that matter, and places a job in a prompt queue. This chapter follows that lifecycle so the reader can tell a submission error from a runtime failure, understand why some visible branches do not run, and interpret the progress, history, and interruption signals around the executor.

Admission: the server turns a graph into a job

The first transition is from an editable graph to a submitted prompt. The server receives JSON containing the workflow and may also receive a client identifier, ordering information, partial execution targets, and extra data. If the caller does not provide a prompt identifier, the server creates one. That identifier follows the job through the queue, WebSocket messages, errors, and history. It gives a client a stable way to distinguish its result from another request that may be running at the same time.

Before a job can enter the queue, ComfyUI applies any registered node replacements and validates the prompt. This is the point at which the graph contract from the previous chapter becomes an admission test. The validator checks that each node has a known type, that required inputs are present, that literal values can be converted and stay within declared limits, and that linked outputs are compatible with the inputs that consume them. It follows linked ancestors recursively rather than checking each box in isolation, because an invalid upstream value makes a downstream result unreliable as well.

The validator also checks the graph's shape. A link must point to a node and output slot that exist. A dependency cycle cannot provide the first result needed to begin a normal evaluation. If the prompt contains several output nodes, a valid path may survive even when a different output path is broken; the server can report node errors while still retaining a usable output. If no output path can be validated, the request is rejected rather than being placed in a queue that cannot produce a requested result.

This early rejection is valuable because it keeps different kinds of failure separate. A missing node or incompatible type is a problem in the submitted data. A checkpoint that cannot be loaded, a VAE that cannot process a latent shape, or a custom node that raises an exception is a later problem in execution or in the component being invoked. The boundaries are not absolute—some semantic errors cannot be known from types alone—but the distinction gives a person a useful first diagnostic question: did the server accept the graph, or did the graph fail before it became a job?

Validation is also a dependency operation, not just a form check. To validate an output, ComfyUI follows each linked input toward its producer and validates the producer before treating its value as available. A downstream sampler cannot be considered sound if the model feeding it is missing, and a save node cannot be considered sound if the image path that feeds it has an invalid link. The recursion lets the server attach errors to the node where the contract breaks while retaining the relationship to any outputs that depend on it.

The result can be selective when a prompt has more than one output. One output path may be valid even if another path refers to a missing node. The server can preserve the valid path and report the invalid one rather than treating the entire canvas as an indivisible command. A caller can also request partial execution targets, narrowing which output roots are considered for this job. That does not rewrite the workflow; it changes which consequences of the workflow the current request asks the executor to produce.

Several checks happen before the expensive part of generation. Literal values are converted to their declared basic types, ranges are checked, and linked values are compared with the consumer's accepted types. Custom validation hooks may add rules that a generic type cannot express. Cycle detection looks for a structural path that can never supply a first result. These checks cannot predict every model-specific failure, but they move obvious problems to the cheapest stage at which ComfyUI can explain them.

Output selection is part of admission. A node marked as an output node tells ComfyUI that the result is worth producing and that the dependencies feeding it are relevant. The validator collects those output roots, or narrows them when the request specifies partial execution targets. It does not treat every node on the canvas as a command. An alternate sampler, an experimental branch, or an intermediate node may remain in the serialized graph without becoming part of this particular job if no selected output depends on it.

That behavior is easier to understand as a consequence of ownership. The client owns the submitted graph and its requested targets. The server owns request identity, validation, and queue insertion. The executor owns the graph's execution state. The output declaration connects the first responsibility to the third: it tells the executor which part of the client's graph is currently being requested. The server does not need to guess a user's intention from the position of boxes on a canvas.

If validation succeeds, the server packages the prompt into a queue item. Alongside the prompt and its identifier, the item carries an ordering number, extra execution data, the output nodes to execute, and sensitive values kept separately from ordinary queue data. The queue can therefore order work and later expose useful status without treating credentials as ordinary workflow content. The request has now changed state: it is no longer just a graph in an HTTP body, but a job waiting for a worker.

Execution: the job becomes a set of ready dependencies

The queue is not the schedule itself. When a worker takes a job, it constructs a prompt executor and prepares execution state for that prompt. The executor creates a dynamic view of the graph and an execution list. The list starts from the selected output nodes and walks backward through non-lazy links to discover the dependencies that must be available. It then represents those dependencies as a set of pending nodes with counts of how many required predecessors still block each one.

This is a topological schedule. A node becomes ready when its blocking dependencies have completed. The visual graph may run from left to right, right to left, or in several branches; the schedule follows links and readiness, not canvas coordinates. If two branches depend on the same upstream value, that value must become available first. If two branches are independent, both can become ready without one being logically downstream of the other. The executor can choose among ready nodes using small user-facing heuristics, including preferring an output node or an asynchronous node, but those preferences do not replace dependency order.

The state transition for an ordinary node is therefore conditional. It begins pending, waits until its required upstream links are ready, receives evaluated inputs, and runs its declared function. On success, the executor stores the node's outputs and releases downstream nodes. On failure, it records the node that failed and the values it could safely format for an error response. A node that is present in the original graph but not on a required path remains outside this execution list. Presence on the canvas and participation in the job are different facts.

Consider a graph with one checkpoint feeding two alternative sampling branches, each ending in its own preview output. The checkpoint must be available before either sampler can run, so both branches are initially blocked by the same predecessor. Once that predecessor completes, the branches can become ready independently. If only one preview is selected as a partial target, the other branch need not enter this job at all. The schedule is therefore a consequence of the selected roots and the dependency counts, not a fixed script hidden behind the editor.

The executor also has to distinguish a node that is ready from a node that has merely been named. When a predecessor completes, the execution list releases the downstream edges that depended on it. When a node is staged, a dynamic behavior can still reveal that another input is required, so the node may return to the pending graph instead of being marked complete. That possibility is why the lifecycle is better understood as controlled state transitions than as a single pass through a list. The graph can be static at submission while the actual work selected by it remains conditional.

A static cycle is usually caught during validation. If a cycle appears only after a node creates more graph during execution, the executor can discover that no pending node can become ready and report a dependency-cycle error at runtime. This difference matters for diagnosis. A pre-submission cycle is a malformed prompt shape. A runtime cycle is a failure of a dynamic behavior that was not fully visible in the initial graph. Both are graph failures, but they occur at different transitions and therefore produce different evidence about where to look.

As nodes run, the executor and server communicate with the client through events. A WebSocket client first receives queue status and can then receive messages identifying the currently executing node, node completion, cached-node information when applicable, progress, previews, and errors. The progress registry associates updates with the prompt and the node, and can also preserve the relationship between a temporary dynamic node and the visible node that created it. This lets a frontend display activity without taking ownership of the graph scheduler.

These events describe different kinds of state. An “executing” message says what the worker is doing now; a progress update says how a node's longer operation is advancing; a preview is an intermediate visual observation; and a completion or error message says what happened to a particular execution path. They are useful for a live interface, but they are not the durable result by themselves. A client that reconnects needs the prompt identifier and the server's current status to understand which job it is observing, while a client that wants the final output can ask history after the run has ended.

The distinction between visible and internal identity becomes important when a node creates temporary work. Progress can carry the dynamic node's own identifier, the visible node associated with it, and the original node that caused it to exist. A frontend can therefore show meaningful activity even though the executor is operating on more nodes than the user placed on the canvas. The server is translating execution state for observation; it is not changing the dependency rules to suit the display.

The HTTP routes provide a complementary, less transient view. A client can inspect the queue, retrieve history for a prompt identifier, fetch output files, request node metadata, or ask the server to interrupt a running prompt. The queue itself retains running and pending work, while history records completed results and status information. The client therefore does not have to capture every WebSocket event to obtain the final artifact, although the event stream is what makes live progress and early failure visible.

Interruption illustrates why the prompt identifier matters. A client can request interruption for a specific running prompt, while the queue also supports clearing or deleting pending items. The running executor and the queue coordinate these actions: a pending item can be removed before execution, while a running item must be signaled so the current processing path can stop. The server's targeted interruption logic checks that the identifier still belongs to the running job rather than applying a stale request to a different job that started later.

The lifecycle can be read as a sequence of ownership transfers and possible exits:

sequenceDiagram participant C as Client participant S as Server participant V as Validator participant Q as Prompt queue participant X as Prompt executor participant H as History C->>S: POST serialized workflow S->>V: Validate and choose output targets alt invalid graph V-->>S: Node and prompt errors S-->>C: Rejection response else valid graph V-->>S: Valid output targets S->>Q: Enqueue prompt id and execution state Q->>X: Start queued job X-->>C: WebSocket execution and progress events X->>H: Store status and outputs C->>S: Request history or output S-->>C: Result and file references end

The sequence shows why ComfyUI can support both an interactive editor and an external application. The client submits the same kind of graph either way. The server validates and queues it. The executor owns dependency release and node execution. The history and output routes provide durable retrieval, while the WebSocket carries observations of a process that is still in motion. The UI is a participant in the lifecycle, not the owner of its core semantics.

That separation also explains the practical value of early validation. A rejected graph never consumes the model-loading and sampling work reserved for a valid job. A queued graph can be observed by its prompt identifier even when its visible result has not appeared. A completed graph can be retrieved after the live connection is gone. And a graph with multiple output roots can be narrowed to the result of interest without needing a second, differently structured workflow.

The lifecycle is deliberately indifferent to whether the eventual output is an image, a video, an audio object, or another supported result. It sees typed nodes, dependencies, output roots, and execution state. The next step is to put a familiar generation path inside that lifecycle: which model components enter the graph, where text becomes conditioning, where a latent is sampled, and how the final artifact returns through the output boundary.

References