comfyanonymous/ComfyUI
Patterns and conventions
Idioms that recur across the ComfyUI codebase. Follow them when adding new code; deviate only when there is a concrete reason.
File and module layout
- One Python module per node feature/family in
comfy_extras/—nodes_<topic>.py. The module exposesNODE_CLASS_MAPPINGS(V1) or acomfy_entrypoint()returning aComfyExtension(V3). - One Python module per model architecture in
comfy/ldm/<model>/, with the network code split betweenmodel.py,mmdit.py,vae.py, etc. as appropriate. - One file per text encoder family in
comfy/text_encoders/, often paired with a tokenizer JSON config in the same directory. - One file per built-in API integration in
comfy_api_nodes/nodes_<provider>.py, sharing helpers fromcomfy_api_nodes/util/.
Logging instead of print
pyproject.toml enables Ruff's T rules (no print). Use the logging module — import logging and logging.info(...). The logger is configured in app/logger.py and writes to stderr by default (or stdout with --log-stdout).
Use torch.nn from comfy.ops, not from torch
Model code should import Linear, Conv2d, LayerNorm, etc. from comfy.ops (or from a sibling module that re-exports them) rather than from torch.nn. This routes through disable_weight_init, manual_cast, and MixedPrecisionOps so weight loading, dtype, and quantization stay consistent. See comfy/ops.py for the full set.
Device and dtype
- Use
comfy.model_management.get_torch_device()to pick the active device. Don't hardcode"cuda". - Use
comfy.model_management.intermediate_device()andcomfy.model_management.intermediate_dtype()for tensors that flow between nodes (the device/dtype where node outputs should live so that the next node can pick them up cheaply). Seecomfy_extras/nodes_canny.pyfor an idiomatic example. - Wrap weight movement through a
ModelPatcher(comfy/model_patcher.py). Don't move parameters off-device manually.
Caching key correctness
A node's input signature plus an IS_CHANGED (or fingerprint_inputs in V3) hash determines whether the executor reuses cached output. If a node has hidden state (a random seed, a file mtime, etc.) it must declare that via IS_CHANGED so the executor invalidates correctly. See comfy_execution/caching.py.
Thread safety
Node FUNCTION methods run on the single prompt-worker thread. They can assume single-threaded access to the model and to global state. The HTTP server and prompt worker live in different threads, but they communicate via the PromptQueue; don't reach across.
Async vs sync
The HTTP server is async (aiohttp). The executor's execute is a coroutine and node FUNCTION methods can be either sync or async def. The V3 execute is async by convention. Avoid asyncio.run inside node code — the loop is already running. Use await if you're already in async context, or asyncio.run_until_complete only at clear boundaries (e.g., when invoking sync code from a sync FUNCTION).
For sync code that needs to call into the async public API, see comfy_api.internal.async_to_sync.create_sync_class (comfy_api/internal/async_to_sync.py).
Sanitize inputs
User-supplied filenames are validated against folder_paths.get_directory_by_type and absolute-path checks before opening or reading. See _collect_output_absolute_paths in main.py for the pattern of "compute absolute path, then verify it stays under the expected base directory."
Don't import torch at module top before main.py runs
main.py sets several env vars (CUDA_VISIBLE_DEVICES, TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL, MIMALLOC_PURGE_DELAY) before importing torch. Modules imported during early startup must not import torch eagerly. The runtime checks for this and warns: "WARNING: Potential Error in code: Torch already imported, torch should never be imported before this point."
Type hints and docs
- Use
from __future__ import annotationsin newer files for forward references. - Document node behavior on the class via
DESCRIPTIONandOUTPUT_TOOLTIPS. The frontend surfaces these in the UI. - For data classes, use Pydantic in API-node territory and
TypedDict/regular dataclasses elsewhere.
V1 vs V3 node style
Both styles work and are deeply mixed in the repo. Pick V3 for new nodes:
- V1 —
INPUT_TYPESclassmethod returning a dict,RETURN_TYPEStuple,FUNCTION = "method_name",CATEGORYstring. Most ofnodes.pyis V1. - V3 —
define_schemaclassmethod returningio.Schema(...),executeclassmethod,comfy_entrypoint() → ComfyExtension. Seecomfy_extras/nodes_canny.py.
Error messages should help
When a load path fails, raise with enough context to fix it:
raise RuntimeError(
"ERROR: clip input is invalid: None\n\n"
"If the clip is from a checkpoint loader node your checkpoint "
"does not contain a valid clip or text encoder model."
)(from CLIPTextEncode.encode in nodes.py)
Frontend users see these messages verbatim, so be specific about which input failed and how to fix it.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.