{"record":{"id":"63f2ccd904062193","repo":"invoke-ai/InvokeAI","slug":"unsupported-url-scheme-parts-scheme-only-http","errorCode":null,"errorMessage":"Unsupported URL scheme '{parts.scheme}'. Only http and https are allowed.","messagePattern":"Unsupported URL scheme '(.+?)'\\. Only http and https are allowed\\.","errorType":"validation","errorClass":"UnsafeDownloadURLException","httpStatus":400,"severity":"error","filePath":"invokeai/app/util/ssrf.py","lineNumber":200,"sourceCode":"            \"Set `allow_private_download_urls` in invokeai.yaml to permit downloads from loopback \"\n            \"and private-network addresses.\"\n        )\n\n\ndef validate_download_url(url: str, allow_private_urls: bool = False) -> None:\n    \"\"\"Reject `url` up front if it obviously points somewhere only the server can reach.\n\n    Every address the host resolves to must be public — a hostname with both a public and a\n    loopback record is rejected, because we cannot control which one the HTTP client picks.\n\n    An unresolvable host is allowed through to the HTTP client, so that offline test\n    environments and mocked sessions keep working. That is only safe because the session\n    from `build_guarded_session()` re-checks the address it actually connects to.\n    \"\"\"\n    parts = urlsplit(str(url))\n\n    if parts.scheme.lower() not in ALLOWED_SCHEMES:\n        raise UnsafeDownloadURLException(f\"Unsupported URL scheme '{parts.scheme}'. Only http and https are allowed.\")\n\n    host = parts.hostname\n    if not host:\n        raise UnsafeDownloadURLException(f\"Download URL '{url}' has no host.\")\n\n    try:\n        port = parts.port\n    except ValueError as e:\n        raise UnsafeDownloadURLException(f\"Download URL '{url}' has an invalid port.\") from e\n\n    if allow_private_urls:\n        return\n\n    for spelling in _host_spellings(host):\n        literal = _parse_ipv4_literal(spelling)\n        if literal is not None:\n            candidates = [literal]\n        else:","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/util/ssrf.py#L182-L218","documentation":"validate_download_url only permits http and https (ALLOWED_SCHEMES). Any other URL scheme (ftp, file, data, etc.) is rejected up-front with UnsafeDownloadURLException before any DNS or connection work.","triggerScenarios":"Passing a download URL like ftp://host/model.safetensors, file:///path, or data: URI to the download API; a URL with no scheme (urlsplit leaves scheme empty, which is not in ALLOWED_SCHEMES).","commonSituations":"Copy-pasting an FTP link from a model index; attempting local file:// downloads through the remote-download path; missing 'http://' prefix so scheme parses as empty.","solutions":["Use an http:// or https:// URL for the download","Download from non-http sources manually and place the file in the models directory","Add the scheme (e.g. https://) if it was omitted from the URL","Check the URL for typos or hidden characters corrupting the scheme"],"exampleFix":"// before\nurl = 'ftp://example.com/model.safetensors'\n// after\nurl = 'https://example.com/model.safetensors'","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\n\ndef validate_scheme(url):\n    scheme = urlsplit(str(url)).scheme.lower()\n    if scheme not in ('http', 'https'):\n        raise ValueError(f\"scheme must be http/https, got '{scheme}'\")","typeGuard":null,"tryCatchPattern":"from invokeai.app.util.ssrf import UnsafeDownloadURLException\ntry:\n    download(url)\nexcept UnsafeDownloadURLException as e:\n    if 'Unsupported URL scheme' in str(e):\n        url = 'https://' + str(url).split('://', 1)[-1]\n        download(url)\n    else:\n        raise","preventionTips":["Always prefix URLs with https:// before submitting","Reject ftp/file/data URLs at input boundaries","Normalize URLs (strip whitespace) before validation"],"tags":["security","ssrf","url","validation"],"backgroundTag":"unsupported-url-scheme","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}