unslothai/unsloth · error · FileNotFoundError

unknown ControlNet '{spec_id}': not a local model, catalog e

Error message

unknown ControlNet '{spec_id}': not a local model, catalog entry, or HF repo id

What it means

resolve_controlnet() failed all three resolution paths: spec_id is not in the merged catalog (curated + local scan), not a curated repo_id, and does not match the strict 'owner/name' Hugging Face repo shape. The strict regex deliberately rejects anything that looks like a filesystem path so it can never reach from_pretrained.

Source

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

        if entry.families and fam and fam not in {f.lower() for f in entry.families}:
            raise ValueError(
                f"ControlNet '{spec_id}' is for {', '.join(entry.families)}, not the loaded "
                f"'{family}' model; pick a ControlNet built for this family."
            )
        if entry.source == "local":
            path = entry.local_path or ""
            if not path or not Path(path).is_dir():
                raise FileNotFoundError(f"ControlNet '{spec_id}' is no longer present on disk")
            return ResolvedControlNet(spec_id, path, is_local = True)
        if not entry.repo_id:
            raise ValueError(f"ControlNet '{spec_id}' has no repo")
        return ResolvedControlNet(spec_id, entry.repo_id, is_local = False)

    # A bare HF repo id (owner/name). STRICT shape so a filesystem-looking id can never reach from_pretrained.
    if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*", spec_id):
        return ResolvedControlNet(spec_id, spec_id, is_local = False)

    raise FileNotFoundError(
        f"unknown ControlNet '{spec_id}': not a local model, catalog entry, or HF repo id"
    )


# Union ControlNet mode indices: a union model selects its head via an integer ``control_mode``.
_UNION_CONTROL_MODES: dict[str, int] = {
    "canny": 0,
    "tile": 1,
    "depth": 2,
    "blur": 3,
    "pose": 4,
    "gray": 5,
    "lq": 6,
}


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

View on GitHub (pinned to 203007d190)

Solutions

  1. List valid ids with list_controlnets(family=...) and use one of those ids
  2. If you meant a Hugging Face model, pass the bare repo id exactly as 'owner/name' (no scheme, no host, no path separators beyond one '/')
  3. If you meant a local folder, place it in the scanned models directory with config.json plus weights so it appears in the catalog
  4. Routes should map this FileNotFoundError to a 400 so the client sees the message

Example fix

# before
resolve_controlnet("https://huggingface.co/lllyasviel/sd-controlnet-canny")

# after
resolve_controlnet("lllyasviel/sd-controlnet-canny")
Defensive patterns

Strategy: validation

Validate before calling

import re
from studio.backend.core.inference.diffusion_controlnet import list_controlnets

HF_REPO = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*")

def controlnet_id_valid(spec_id: str) -> bool:
    if spec_id in {e.id for e in list_controlnets()}:
        return True
    return bool(HF_REPO.fullmatch(spec_id))

Try / catch

try:
    resolved = resolve_controlnet(spec_id, family=fam)
except FileNotFoundError as e:
    return bad_request(str(e))  # message lists all three accepted shapes

Prevention

When it happens

Trigger: Calling resolve_controlnet() with a typo'd id, a bare model name not present in the catalog, an absolute/relative path ('/data/cnet', './cnet', 'C:\\cnet'), or an id containing spaces or invalid characters. Paths and single-segment names fail the r'[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*' fullmatch.

Common situations: Client sends a local filesystem path expecting a direct load; stale UI offering an entry removed from the catalog; copy-paste of a repo URL (huggingface.co/owner/name) instead of the bare repo id; uppercase/underscore typos.

Related errors


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