unslothai/unsloth · error · ValueError

Unknown control type {control_type!r} for a union ControlNet

Error message

Unknown control type {control_type!r} for a union ControlNet. Use one of: {', '.join(sorted(_UNION_CONTROL_MODES))}, or passthrough.

What it means

union_control_mode() was asked for a control type that is not one of the seven union head indices (blur, canny, depth, gray, lq, pose, tile) nor ''/'passthrough'. Union ControlNet models pick their head via an integer control_mode, so an unrecognized type would silently run the wrong head; this ValueError makes the route return 400 instead.

Source

Thrown at studio/backend/core/inference/diffusion_controlnet.py:222

def union_control_mode(spec_id: str, control_type: str) -> Optional[int]:
    """The integer ``control_mode`` for a union ControlNet, or None.

    A union model requires a concrete mode. A known mode maps to its index; ``passthrough`` (or
    empty) defaults to 0 (canny head). An unknown/typo'd type raises ValueError so the route
    returns a 400 instead of running the wrong head. A non-union entry returns None."""
    entry = _catalog_by_id().get(spec_id)
    if entry is None:
        # A union model may be named by its bare repo id, so match on repo_id too (the catalog is keyed by short id), else the union runs the wrong head.
        entry = next((e for e in _CURATED if e.repo_id and e.repo_id == spec_id), None)
    if entry is None or not entry.is_union:
        return None
    ct = (control_type or "").strip().lower()
    if ct in _UNION_CONTROL_MODES:
        return _UNION_CONTROL_MODES[ct]
    if ct in ("", "passthrough"):
        return 0  # preprocessed map, no intrinsic mode; canny is the default head
    raise ValueError(
        f"Unknown control type {control_type!r} for a union ControlNet. Use one of: "
        f"{', '.join(sorted(_UNION_CONTROL_MODES))}, or passthrough."
    )


def preprocess_control(image: Any, control_type: str) -> Any:
    """Turn a source image into a control map.

    ``passthrough`` returns the image unchanged. ``canny`` derives a dependency-free gradient edge
    map (a rough stand-in for true Canny). Unknown types pass through so a new type never fails.
    """
    ct = (control_type or "passthrough").strip().lower()
    if ct != "canny":
        return image
    import numpy as np
    from PIL import Image

    gray = np.asarray(image.convert("L"), dtype = np.float32)

View on GitHub (pinned to 203007d190)

Solutions

  1. Use one of the supported types: blur, canny, depth, gray, lq, pose, tile — or 'passthrough' (empty string also works and maps to the canny head)
  2. Check the entry's control_types / is_union via list_controlnets() before submitting
  3. Frontend: build the dropdown from sorted(_UNION_CONTROL_MODES) + 'passthrough' instead of a hardcoded generic list

Example fix

# before
mode = union_control_mode("xinsir-controlnet-union", "scribble")

# after
mode = union_control_mode("xinsir-controlnet-union", "canny")
# or, for an already-preprocessed control map:
mode = union_control_mode("xinsir-controlnet-union", "passthrough")
Defensive patterns

Strategy: validation

Validate before calling

from studio.backend.core.inference.diffusion_controlnet import _UNION_CONTROL_MODES

def union_control_type_valid(control_type: str) -> bool:
    ct = (control_type or "passthrough").strip().lower()
    return ct in _UNION_CONTROL_MODES or ct in ("", "passthrough")

Try / catch

try:
    mode = union_control_mode(spec_id, control_type)
except ValueError as e:
    return bad_request(str(e))  # lists the accepted types verbatim

Prevention

When it happens

Trigger: Calling union_control_mode(spec_id_of_union_model, control_type) with a typo ('canny-edge', 'depth-midas', 'Pose') or a type only valid for non-union models (e.g. 'scribble', 'hed'); only raises when the resolved entry has is_union=True.

Common situations: Frontend dropdown sends a preprocessing type the union model does not have; client reuses control_type values from a single-purpose ControlNet against a union model; trailing whitespace/case variants are handled, but invented names are not.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/16feeb87f2537967. Report an issue: GitHub.