comfyanonymous/ComfyUI
Prompt execution
How a JSON prompt becomes node outputs. This page covers the entire flow from POST /prompt to the WebSocket executed event.
Purpose
The prompt executor turns a graph description into a sequence of (node_class, inputs) → outputs calls, with caching so unchanged work is reused, validation so malformed prompts fail fast, and progress reporting so the user sees something happening.
Components
graph LR
Client -->|POST /prompt| Server[server.PromptServer]
Server -->|validate_prompt| Validator[execution.validate_prompt]
Server -->|put| Q[server.PromptQueue]
Q -->|get| Worker[main.prompt_worker thread]
Worker -->|execute| PE[execution.PromptExecutor]
PE --> Graph[comfy_execution.graph<br/>DynamicPrompt + ExecutionList]
PE --> Cache[comfy_execution.caching<br/>CacheSet]
PE --> Nodes[NODE_CLASS_MAPPINGS]
PE -->|send_sync progress, executed| Server
Server -->|ws| ClientThe split between validate_prompt (synchronous, runs on the server thread) and PromptExecutor.execute (async, runs on the worker thread) is intentional: bad prompts get rejected before they consume a queue slot.
Data structures
| Name | File | Role |
|---|---|---|
prompt |
the JSON object posted to /prompt |
{node_id: {"class_type": "...", "inputs": {...}}} |
DynamicPrompt |
comfy_execution/graph.py |
Mutable view; supports node expansion via ephemeral nodes |
ExecutionList |
comfy_execution/graph.py |
DAG ready-set: yields nodes whose inputs are satisfied |
CacheSet |
execution.py + comfy_execution/caching.py |
Holds outputs cache + objects cache (instances of node classes) |
IsChangedCache |
execution.py |
Per-prompt memo of IS_CHANGED / fingerprint_inputs results |
PromptQueue |
server.py |
Priority queue of pending prompts |
PromptExecutor |
execution.py |
The orchestrator |
Step by step
sequenceDiagram
autonumber
participant FE as Frontend
participant Srv as PromptServer
participant Q as PromptQueue
participant W as prompt_worker
participant PE as PromptExecutor
participant N as Node
FE->>Srv: POST /prompt
Srv->>Srv: validate_prompt(prompt)
Srv->>Q: put((number, prompt_id, prompt, extra_data, ...))
Q-->>W: blocking get(timeout)
W->>PE: e.execute(prompt, prompt_id, extra_data, sensitive)
PE->>PE: build DynamicPrompt + ExecutionList
loop ready node
PE->>PE: cache key from inputs + IS_CHANGED
alt cached
PE-->>PE: reuse outputs
else miss
PE->>N: NODE_CLASS_MAPPINGS[type]() (cached in objects)
PE->>N: get_input_data + _async_map_node_over_list
N-->>PE: outputs (single or list)
PE->>Srv: send_sync("executing", {node})
PE->>Srv: send_sync("progress", {value, max})
opt UI output
N-->>PE: ui dict (images, gifs, …)
PE->>Srv: send_sync("executed", history_result)
end
end
end
PE-->>W: history_result, success
W->>Q: task_done(item_id, history_result, status)
W->>Srv: send_sync("status", queue_remaining)Output node selection: the executor finds nodes with OUTPUT_NODE = True and walks ancestors. Anything not connected to an output node is dead code and isn't run. Anything that gets cached doesn't run again until its inputs (or IS_CHANGED) change.
Caching
There are two caches in a CacheSet:
outputs— the actual returned values from a node run (what gets reused).objects— the instantiated node objects themselves (whatNODE_CLASS_MAPPINGS[type]()returned), so a node with expensive__init__only initializes once.
Cache keys are derived two ways depending on the cache:
CacheKeySetID—(node_id, class_type)— used for the objects cache where two nodes of the same class with different IDs should be distinct objects.CacheKeySetInputSignature—(class_type, frozenset_of_inputs)plus theIS_CHANGEDvalue — used for the outputs cache where two nodes computing the same thing should share results.
The cache mode (HierarchicalCache, LRUCache, RAMPressureCache, NullCache) is selected at startup based on the --cache-* flags. See the comfy_execution package page for the full table.
Lazy inputs and IS_CHANGED
A node can declare an input as lazy: True. Such inputs aren't evaluated up front — instead, check_lazy_status (V1) or the V3 equivalent is called, returning a list of inputs that do need to be evaluated based on what's already known. The executor pauses the node, evaluates the requested inputs, and resumes.
IS_CHANGED is queried on every cache check. It can return a value or float("nan") to indicate "always re-run." The IsChangedCache memoizes results per prompt so a node with IS_CHANGED based on a file mtime doesn't stat the file twice.
Node expansion
A node can return {"expand": new_subgraph_json, "result": (...)}. The executor:
- Adds the new nodes as ephemeral children of the original node (
DynamicPrompt.add_ephemeral_node). - Re-builds the ready set.
- Runs the children before considering the original node "done."
This is how the Custom Sampler graph and similar composable nodes work without baking their structure into the executor.
Sensitive inputs
SENSITIVE_EXTRA_DATA_KEYS = ("auth_token_comfy_org", "api_key_comfy_org") (defined in execution.py). The server strips these from queue items returned to clients (_remove_sensitive_from_queue in server.py) but the executor injects them back into extra_data when running a node so API nodes can authenticate.
History
Each completed (or errored) prompt produces a history_result: a dict keyed by node ID containing the node's outputs plus any ui payload. The result is added to the in-memory history, accessible via GET /history/<prompt_id>. The executor also calls register_output_files (from the assets package) so written files become asset references when --enable-assets is on.
Integration points
- Triggered by
server.pyonPOST /prompt. - Runs on the
prompt_workerthread set up inmain.py. - Calls into the entire diffusion engine through node
FUNCTIONmethods. - Streams events to the frontend through
PromptServer.send_sync. - Plays with the Asset system via
register_output_files.
Where to start a change
- Adding a new cache mode: see
comfy_execution/. - Tweaking how inputs are validated:
validate_node_inputincomfy_execution/validation.py. - New executor event: extend
send_synccalls and the WebSocket message types; corresponding tests live intests-unit/execution_test/andtests/execution/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.