{"record":{"id":"cbbe21e80ee16ef8","repo":"invoke-ai/InvokeAI","slug":"invalid-external-model-source-source-stripped","errorCode":null,"errorMessage":"Invalid external model source: '{source_stripped}'","messagePattern":"Invalid external model source: '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"invokeai/app/services/model_install/model_install_default.py","lineNumber":883,"sourceCode":"            except ValueError:\n                pass\n\n            return [RemoteModelFile(url=self._normalize_huggingface_blob_url(source.url), path=Path(\".\"), size=0)], None\n\n        raise Exception(f\"No files associated with {source}\")\n\n    def _guess_source(self, source: str) -> ModelSource:\n        \"\"\"Turn a source string into a ModelSource object.\"\"\"\n        variants = \"|\".join(ModelRepoVariant.__members__.values())\n        hf_repoid_re = f\"^([^/:]+/[^/:]+)(?::({variants})?(?::/?([^:]+))?)?$\"\n        source_obj: Optional[StringLikeSource] = None\n        source_stripped = source.strip('\"')\n\n        if source_stripped.startswith(\"external://\"):\n            external_id = source_stripped.removeprefix(\"external://\")\n            provider_id, _, provider_model_id = external_id.partition(\"/\")\n            if not provider_id or not provider_model_id:\n                raise ValueError(f\"Invalid external model source: '{source_stripped}'\")\n            source_obj = ExternalModelSource(provider_id=provider_id, provider_model_id=provider_model_id)\n        elif Path(source_stripped).exists():  # A local file or directory\n            source_obj = LocalModelSource(path=Path(source_stripped))\n        elif match := re.match(hf_repoid_re, source):\n            source_obj = HFModelSource(\n                repo_id=match.group(1),\n                variant=ModelRepoVariant(match.group(2)) if match.group(2) else None,  # pass None rather than ''\n                subfolder=Path(match.group(3)) if match.group(3) else None,\n            )\n        elif re.match(r\"^https?://[^/]+\", source):\n            source_obj = URLModelSource(\n                url=Url(source),\n            )\n        else:\n            raise ValueError(f\"Unsupported model source: '{source}'\")\n        return source_obj\n\n    # --------------------------------------------------------------------------------------------","sourceCodeStart":865,"sourceCodeEnd":901,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/model_install/model_install_default.py#L865-L901","documentation":"_guess_source() parses a free-form string into a ModelSource. For strings starting with 'external://', it splits into provider_id/model_id on '/'; if either part is missing (no slash, empty provider, or empty model id), it raises ValueError(\"Invalid external model source: '...'\").","triggerScenarios":"heuristic_import('external://') or 'external://openai' (missing model id or provider); extra slashes like 'external://provider/' or 'external:///model'; whitespace/quote handling leaves a malformed string; typo such as 'external:/provider/model'.","commonSituations":"UI/CLI users typing an external:// shorthand manually; templated strings where the model id variable is empty; provider ids containing slashes (not supported by the simple partition).","solutions":["Use the full form 'external://<provider_id>/<provider_model_id>' with exactly one slash separating non-empty parts.","Check that the provider supports the model and that the provider_model_id is correct (e.g. 'external://openai/dall-e-3').","If the id contains slashes, avoid the external:// string form and pass ExternalModelSource(provider_id=..., provider_model_id=...) directly.","Trim whitespace/quotes from user input before calling heuristic_import()."],"exampleFix":"// before\nservice.heuristic_import('external://openai')\n// after\nservice.heuristic_import('external://openai/dall-e-3')","handlingStrategy":"validation","validationCode":"import re\nEXTERNAL_RE = re.compile(r'^external://[^/]+/[^/]+/?$')\nif source.startswith('external://') and not EXTERNAL_RE.match(source.strip('\"')):\n    raise ValueError('Use external://<provider_id>/<provider_model_id>')","typeGuard":"def is_valid_external_source(s: str) -> bool:\n    s = s.strip('\"')\n    if not s.startswith('external://'):\n        return False\n    provider, _, model_id = s.removeprefix('external://').partition('/')\n    return bool(provider) and bool(model_id)","tryCatchPattern":"try:\n    source_obj = service.heuristic_import(user_input)\nexcept ValueError as e:\n    if 'Invalid external model source' in str(e):\n        source_obj = None  # prompt user for provider/model-id","preventionTips":["Always emit external:// sources as 'provider/model' with both parts non-empty.","Trim quotes/whitespace from user input before parsing.","For provider ids containing slashes, construct ExternalModelSource directly.","Validate UI-generated strings with a regex before submission."],"tags":["validation","parsing","valueerror"],"backgroundTag":"invalid-model-source-format","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}