Comfy-Org/ComfyUI · error · RuntimeError

Qwen Fun ControlNet requires a control hint image.

Error message

Qwen Fun ControlNet requires a control hint image.

What it means

After resolving the base model, the Qwen Fun ControlNet must convert a control image ('hint') into hint tokens; _process_hint_tokens returns None when no usable hint was passed, and the code raises RuntimeError immediately. The hint is the structural conditioning (depth/pose/canny/etc.) and is mandatory for Fun controlnets.

Source

Thrown at comfy/ldm/qwen_image/controlnet.py:133

        attention_mask=None,
        guidance: torch.Tensor = None,
        hint=None,
        transformer_options={},
        base_model=None,
        **kwargs,
    ):
        if base_model is None:
            raise RuntimeError("Qwen Fun ControlNet requires a QwenImage base model at runtime.")

        encoder_hidden_states_mask = attention_mask
        # Keep attention mask disabled inside Fun control blocks to mirror
        # VideoX behavior (they rely on seq lengths for RoPE, not masked attention).
        encoder_hidden_states_mask = None

        hidden_states, img_ids, _ = base_model.process_img(x)
        hint_tokens = self._process_hint_tokens(hint)
        if hint_tokens is None:
            raise RuntimeError("Qwen Fun ControlNet requires a control hint image.")

        if hint_tokens.shape[1] != hidden_states.shape[1]:
            max_tokens = min(hint_tokens.shape[1], hidden_states.shape[1])
            hint_tokens = hint_tokens[:, :max_tokens]
            hidden_states = hidden_states[:, :max_tokens]
            img_ids = img_ids[:, :max_tokens]

        txt_start = round(
            max(
                ((x.shape[-1] + (base_model.patch_size // 2)) // base_model.patch_size) // 2,
                ((x.shape[-2] + (base_model.patch_size // 2)) // base_model.patch_size) // 2,
            )
        )
        txt_ids = torch.arange(txt_start, txt_start + context.shape[1], device=x.device).reshape(1, -1, 1).repeat(x.shape[0], 1, 3)
        ids = torch.cat((txt_ids, img_ids), dim=1)
        image_rotary_emb = base_model.pe_embedder(ids).to(x.dtype).contiguous()

        hidden_states = base_model.img_in(hidden_states)

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Connect a control image (or preprocessor output) to the control image input of the Fun ControlNet apply node.
  2. Un-bypass/unmute any preprocessor nodes feeding the hint.
  3. Verify the image tensor is non-empty and has the expected CHW/BHWC layout the apply node expects.
  4. If you did not intend image control, remove the Fun ControlNet from the conditioning chain entirely.
Defensive patterns

Strategy: validation

Validate before calling

def validate_hint(hint):
    import torch
    if hint is None or (torch.is_tensor(hint) and hint.numel() == 0):
        raise ValueError("Fun ControlNet needs a non-empty control image; check preprocessor wiring")
    return hint

Type guard

def has_control_image(hint) -> bool:
    import torch
    return torch.is_tensor(hint) and hint.numel() > 0 and hint.dim() >= 3

Prevention

When it happens

Trigger: Applying Qwen Fun ControlNet through the proper apply node but leaving the control image input empty; passing hint=None via custom code; the control-image preprocessor node is bypassed so None flows into the apply node.

Common situations: Workflow copied from a text-only example where the image input was never wired; preprocessor (e.g. depth estimator) muted/bypassed; empty image mask or a load-image node pointing at a missing file yielding None.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/72cd23966b28e787. Report an issue: GitHub.