unslothai/unsloth · error · ValueError
ControlNet '{spec_id}' is for {', '.join(entry.families)}, n
Error message
ControlNet '{spec_id}' is for {', '.join(entry.families)}, not the loaded '{family}' model; pick a ControlNet built for this family. What it means
ControlNets are architecture-specific: resolve_controlnet() looks the spec_id up in the curated catalog (including full-repo-id matches) and rejects an entry whose families list does not contain the loaded model's family, before any download happens. The family check also catches full-repo-id lookups so a curated entry named by repo id cannot bypass the gate via the bare-repo fallback.
Source
Thrown at studio/backend/core/inference/diffusion_controlnet.py:171
def resolve_controlnet(spec_id: str, *, family: Optional[str] = None) -> ResolvedControlNet:
"""Resolve a ControlNet id to a loadable repo id / local dir.
Accepts a catalog/local id or a bare HF repo id (``owner/name``); the backend loads it with
``from_pretrained``. Raises on an unknown id (caller maps to 400).
``family`` enforces compatibility: a ControlNet is architecture-specific, so an entry tagged
for another family is rejected here rather than loaded through the wrong pipeline later.
"""
entry = _catalog_by_id().get(spec_id)
if entry is None:
# A curated entry named by its full repo id must still hit the family gate below, not slip through the bare-repo fallback.
entry = next((e for e in _CURATED if e.repo_id and e.repo_id == spec_id), None)
if entry is not None:
# A direct API call could send an entry for another family; reject it before any download.
fam = (family or "").strip().lower()
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"View on GitHub (pinned to 203007d190)
Solutions
- Pick a ControlNet whose catalog entry lists the loaded family (check entry.families).
- After switching the loaded model, re-resolve the ControlNet for the new family before generating.
- Expose family info in your picker UI so CN options filter by the loaded model.
Example fix
# before: SDXL CN with a Flux model loaded
engine.generate(prompt=p, controlnet=("xinsir-controlnet-v1.1-sdxl-canny", img, "canny", 0.8, 0.0, 1.0))
# after: Flux-family CN
engine.generate(prompt=p, controlnet=("instantx/flux-union-controlnet", img, "canny", 0.8, 0.0, 1.0)) Defensive patterns
Strategy: validation
Validate before calling
from core.inference import diffusion_controlnet
def cn_matches_family(spec_id: str, family: str) -> bool:
entry = diffusion_controlnet._catalog_by_id().get(spec_id)
if entry is None:
return False
fam = (family or "").strip().lower()
return not entry.families or not fam or fam in {f.lower() for f in entry.families} Type guard
def controlnet_suitable_for(spec_id: str, family: str) -> bool:
return cn_matches_family(spec_id, family) Try / catch
try:
out = engine.generate(prompt=p, controlnet=(cn_id, img, t, s, gs, ge))
except ValueError as e:
if "is for" in str(e) and "pick a ControlNet built for this family" in str(e):
cn_id = default_cn_for(family=state.family.name) # re-pick from catalog by family
out = engine.generate(prompt=p, controlnet=(cn_id, img, t, s, gs, ge))
else:
raise Prevention
- Filter the CN catalog by entry.families against the loaded family before showing options.
- Clear or re-resolve the selected ControlNet whenever the loaded model changes.
- Never hardcode one CN id across a pipeline that rotates base models.
When it happens
Trigger: Passing a ControlNet id tagged for another family — e.g. an SDXL ControlNet ('xinsir ... sdxl ...') while a FLUX model is loaded, or a Flux Union CN while SDXL is loaded. The loaded family is passed in as family=state.family.name.
Common situations: Switching loaded models without re-picking the ControlNet; curated lists mixing SD/SDXL/Flux entries with similar names; automated pipelines pairing a fixed CN repo with a rotating set of base models.
Related errors
- ControlNet is not supported for the '{fam.name}' model famil
- {workflow} is not supported for the '{state.family.name}' mo
- {_cn_fs.reason}
- Reference images are not supported for the '{state.family.na
- ControlNet currently combines with plain text-to-image only,
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/e058681eebe0a12b.
Report an issue: GitHub.