{"record":{"id":"16feeb87f2537967","repo":"unslothai/unsloth","slug":"unknown-control-type-control-type-r-for-a-union","errorCode":null,"errorMessage":"Unknown control type {control_type!r} for a union ControlNet. Use one of: {', '.join(sorted(_UNION_CONTROL_MODES))}, or passthrough.","messagePattern":"Unknown control type (.+?) for a union ControlNet\\. Use one of: (.+?), or passthrough\\.","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/diffusion_controlnet.py","lineNumber":222,"sourceCode":"\ndef union_control_mode(spec_id: str, control_type: str) -> Optional[int]:\n    \"\"\"The integer ``control_mode`` for a union ControlNet, or None.\n\n    A union model requires a concrete mode. A known mode maps to its index; ``passthrough`` (or\n    empty) defaults to 0 (canny head). An unknown/typo'd type raises ValueError so the route\n    returns a 400 instead of running the wrong head. A non-union entry returns None.\"\"\"\n    entry = _catalog_by_id().get(spec_id)\n    if entry is None:\n        # 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.\n        entry = next((e for e in _CURATED if e.repo_id and e.repo_id == spec_id), None)\n    if entry is None or not entry.is_union:\n        return None\n    ct = (control_type or \"\").strip().lower()\n    if ct in _UNION_CONTROL_MODES:\n        return _UNION_CONTROL_MODES[ct]\n    if ct in (\"\", \"passthrough\"):\n        return 0  # preprocessed map, no intrinsic mode; canny is the default head\n    raise ValueError(\n        f\"Unknown control type {control_type!r} for a union ControlNet. Use one of: \"\n        f\"{', '.join(sorted(_UNION_CONTROL_MODES))}, or passthrough.\"\n    )\n\n\ndef preprocess_control(image: Any, control_type: str) -> Any:\n    \"\"\"Turn a source image into a control map.\n\n    ``passthrough`` returns the image unchanged. ``canny`` derives a dependency-free gradient edge\n    map (a rough stand-in for true Canny). Unknown types pass through so a new type never fails.\n    \"\"\"\n    ct = (control_type or \"passthrough\").strip().lower()\n    if ct != \"canny\":\n        return image\n    import numpy as np\n    from PIL import Image\n\n    gray = np.asarray(image.convert(\"L\"), dtype = np.float32)","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/diffusion_controlnet.py#L204-L240","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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)","Check the entry's control_types / is_union via list_controlnets() before submitting","Frontend: build the dropdown from sorted(_UNION_CONTROL_MODES) + 'passthrough' instead of a hardcoded generic list"],"exampleFix":"# before\nmode = union_control_mode(\"xinsir-controlnet-union\", \"scribble\")\n\n# after\nmode = union_control_mode(\"xinsir-controlnet-union\", \"canny\")\n# or, for an already-preprocessed control map:\nmode = union_control_mode(\"xinsir-controlnet-union\", \"passthrough\")","handlingStrategy":"validation","validationCode":"from studio.backend.core.inference.diffusion_controlnet import _UNION_CONTROL_MODES\n\ndef union_control_type_valid(control_type: str) -> bool:\n    ct = (control_type or \"passthrough\").strip().lower()\n    return ct in _UNION_CONTROL_MODES or ct in (\"\", \"passthrough\")","typeGuard":null,"tryCatchPattern":"try:\n    mode = union_control_mode(spec_id, control_type)\nexcept ValueError as e:\n    return bad_request(str(e))  # lists the accepted types verbatim","preventionTips":["Drive the UI dropdown from sorted(_UNION_CONTROL_MODES) + 'passthrough' instead of a generic control-type list","Normalize control_type with .strip().lower() before sending","Remember only union entries (is_union=True) raise; single-purpose models ignore the type here"],"tags":["controlnet","input-validation","union-model","diffusion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}