Open-Source Wikis

/

ComfyUI

/

How to contribute

/

Patterns and conventions

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 exposes NODE_CLASS_MAPPINGS (V1) or a comfy_entrypoint() returning a ComfyExtension (V3).
  • One Python module per model architecture in comfy/ldm/<model>/, with the network code split between model.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 from comfy_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() and comfy.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). See comfy_extras/nodes_canny.py for 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 annotations in newer files for forward references.
  • Document node behavior on the class via DESCRIPTION and OUTPUT_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:

  • V1INPUT_TYPES classmethod returning a dict, RETURN_TYPES tuple, FUNCTION = "method_name", CATEGORY string. Most of nodes.py is V1.
  • V3define_schema classmethod returning io.Schema(...), execute classmethod, comfy_entrypoint() → ComfyExtension. See comfy_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.

Patterns and conventions – ComfyUI wiki | Factory