{"record":{"id":"b32470b2f06b01e5","repo":"unslothai/unsloth","slug":"controlnet-spec-id-has-no-repo","errorCode":null,"errorMessage":"ControlNet '{spec_id}' has no repo","messagePattern":"ControlNet '(.+?)' has no repo","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/diffusion_controlnet.py","lineNumber":181,"sourceCode":"    entry = _catalog_by_id().get(spec_id)\n    if entry is None:\n        # A curated entry named by its full repo id must still hit the family gate below, not slip through the bare-repo fallback.\n        entry = next((e for e in _CURATED if e.repo_id and e.repo_id == spec_id), None)\n    if entry is not None:\n        # A direct API call could send an entry for another family; reject it before any download.\n        fam = (family or \"\").strip().lower()\n        if entry.families and fam and fam not in {f.lower() for f in entry.families}:\n            raise ValueError(\n                f\"ControlNet '{spec_id}' is for {', '.join(entry.families)}, not the loaded \"\n                f\"'{family}' model; pick a ControlNet built for this family.\"\n            )\n        if entry.source == \"local\":\n            path = entry.local_path or \"\"\n            if not path or not Path(path).is_dir():\n                raise FileNotFoundError(f\"ControlNet '{spec_id}' is no longer present on disk\")\n            return ResolvedControlNet(spec_id, path, is_local = True)\n        if not entry.repo_id:\n            raise ValueError(f\"ControlNet '{spec_id}' has no repo\")\n        return ResolvedControlNet(spec_id, entry.repo_id, is_local = False)\n\n    # A bare HF repo id (owner/name). STRICT shape so a filesystem-looking id can never reach from_pretrained.\n    if re.fullmatch(r\"[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*\", spec_id):\n        return ResolvedControlNet(spec_id, spec_id, is_local = False)\n\n    raise FileNotFoundError(\n        f\"unknown ControlNet '{spec_id}': not a local model, catalog entry, or HF repo id\"\n    )\n\n\n# Union ControlNet mode indices: a union model selects its head via an integer ``control_mode``.\n_UNION_CONTROL_MODES: dict[str, int] = {\n    \"canny\": 0,\n    \"tile\": 1,\n    \"depth\": 2,\n    \"blur\": 3,\n    \"pose\": 4,","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/diffusion_controlnet.py#L163-L199","documentation":"resolve_controlnet() matched a catalog entry whose source is not 'local' but whose repo_id is empty, so there is nothing to download and nothing on disk to load. It is a guard against a malformed curated/catalog entry rather than a user input error.","triggerScenarios":"A catalog entry with source='remote' (or anything != 'local') and repo_id unset (None/'') reaching resolve_controlnet(); typically from editing the _CURATED list or injecting a custom entry with a missing repo_id.","commonSituations":"Hand-editing the curated ControlNet catalog and forgetting the repo_id field; a code change that constructs ControlNetCatalogEntry entries without repo_id; data migration that drops the field.","solutions":["Fix the catalog entry: give it a valid Hugging Face repo_id (owner/name) or convert it to source='local' with a real local_path","If you manage _CURATED in code, add an assertion/test that every non-local entry has a non-empty repo_id","As a caller, catch ValueError and surface it as a configuration error rather than retrying"],"exampleFix":"# before\nControlNetCatalogEntry(id=\"x\", display_name=\"X\", source=\"remote\")\n\n# after\nControlNetCatalogEntry(id=\"x\", display_name=\"X\", source=\"remote\", repo_id=\"owner/x-controlnet\")","handlingStrategy":"validation","validationCode":"from studio.backend.core.inference.diffusion_controlnet import list_controlnets\n\nbad = [e.id for e in list_controlnets() if e.source != \"local\" and not e.repo_id]\nassert not bad, f\"malformed catalog entries without repo_id: {bad}\"","typeGuard":null,"tryCatchPattern":"try:\n    resolved = resolve_controlnet(spec_id, family=fam)\nexcept ValueError as e:\n    log.error(\"catalog misconfigured for %s: %s\", spec_id, e)  # config defect, do not retry","preventionTips":["Assert at startup that every non-local catalog entry has a non-empty repo_id","Review edits to the curated ControlNet list in CI","Never retry this error; it is a data defect, not a transient condition"],"tags":["controlnet","catalog","config-mistake","diffusion"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}