{"record":{"id":"e3788583fea2cf14","repo":"invoke-ai/InvokeAI","slug":"download-url-url-has-an-invalid-port","errorCode":null,"errorMessage":"Download URL '{url}' has an invalid port.","messagePattern":"Download URL '(.+?)' has an invalid port\\.","errorType":"exception","errorClass":"UnsafeDownloadURLException","httpStatus":null,"severity":"error","filePath":"invokeai/app/util/ssrf.py","lineNumber":209,"sourceCode":"    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:\n            try:\n                candidates = _resolve(spelling, port)\n            except (OSError, UnicodeError, ValueError):\n                continue\n        for candidate in candidates:\n            check_address(candidate, host)\n\n\ndef _check_socket(sock: socket.socket) -> None:","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/util/ssrf.py#L191-L227","documentation":"urlsplit().port raises ValueError when the URL's port is non-numeric or out of the allowed range. validate_download_url catches this and re-raises it as UnsafeDownloadURLException, rejecting the URL before any connection is attempted.","triggerScenarios":"URLs like 'https://example.com:99999/file' (port > 65535) or 'https://example.com:abc/file' (non-numeric port) passed to the download API.","commonSituations":"Hand-edited URLs with typo'd ports; copy-paste artifacts like double colons (host::port); generated URLs where a port variable was empty or contained a non-digit character.","solutions":["Fix the port in the URL to a valid number 1-65535","Remove the port entirely to use the scheme default (80/443)","Validate the URL with urllib before submitting it","If ports vary per environment, build the URL programmatically with int(port) validated in range"],"exampleFix":"// before\nurl = 'https://example.com:99999/model.safetensors'  # invalid port\n// after\nurl = 'https://example.com:8443/model.safetensors'","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\n\ndef validate_port(url):\n    try:\n        port = urlsplit(str(url)).port\n    except ValueError:\n        raise ValueError(f\"URL has invalid port: {url}\")\n    if port is not None and not (1 <= port <= 65535):\n        raise ValueError(f\"port out of range: {port}\")","typeGuard":null,"tryCatchPattern":"from invokeai.app.util.ssrf import UnsafeDownloadURLException\ntry:\n    download(url)\nexcept UnsafeDownloadURLException as e:\n    if 'invalid port' in str(e):\n        raise ValueError(f\"Correct the port in: {url}\") from e\n    raise","preventionTips":["Insert ports programmatically as validated ints","Avoid hand-editing ports in URLs","Default to scheme defaults (443/80) unless a specific port is required"],"tags":["security","url","validation","download"],"backgroundTag":"url-invalid-port","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}