{"record":{"id":"7dcaa5e975877433","repo":"BerriAI/litellm","slug":"url-scheme-parsed-scheme-is-not-allowed","errorCode":null,"errorMessage":"URL scheme '{parsed.scheme}' is not allowed","messagePattern":"URL scheme '(.+?)' is not allowed","errorType":"exception","errorClass":"SSRFError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/url_utils.py","lineNumber":265,"sourceCode":"    validated, not the hostname that could rebind. Callers should also disable\n    follow_redirects to prevent redirect-based SSRF bypasses.\n\n    Args:\n        url: The user-supplied URL to validate.\n\n    Returns:\n        Tuple of (rewritten_url, host_header).\n        The rewritten URL has the hostname replaced with the validated IP.\n        The host_header value should be sent as the Host header.\n\n    Raises:\n        SSRFError: If the URL scheme is invalid or the hostname resolves\n            to a private/internal IP address.\n    \"\"\"\n    parsed: Final = urlparse(url)\n\n    if parsed.scheme not in _ALLOWED_SCHEMES:\n        raise SSRFError(f\"URL scheme '{parsed.scheme}' is not allowed\")\n\n    hostname: Final = parsed.hostname\n    if not hostname:\n        raise SSRFError(\"URL has no hostname\")\n\n    port: Final = parsed.port\n    default_port: Final = _default_port_for_scheme(parsed.scheme)\n    effective_port: Final = port if port is not None else default_port\n    host_header: Final = _format_host_header(hostname, effective_port, default_port)\n\n    is_allowlisted: Final = _is_host_allowlisted(hostname, effective_port)\n\n    # Resolve hostname and validate ALL addresses\n    try:\n        addrinfo: Final = socket.getaddrinfo(hostname, effective_port, proto=socket.IPPROTO_TCP)\n    except socket.gaierror as e:\n        raise SSRFError(f\"DNS resolution failed for '{hostname}': {e}\")\n","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/url_utils.py#L247-L283","documentation":"Raised by litellm's SSRF validator (validate_url) when a user-supplied URL's scheme is not in the allowed set (http/https only, per _ALLOWED_SCHEMES). Before doing any DNS resolution or IP validation, litellm restricts fetchable URLs to plain web schemes, so schemes like file://, ftp://, gopher://, or javascript:// are rejected outright. This is a security control protecting the proxy from being used to reach non-web resources.","triggerScenarios":"Calling validate_url / safe_get / async_safe_get with a URL whose scheme is anything other than http or https — e.g. passing 'file:///etc/passwd', 'ftp://host/file', or a malformed URL where urlparse extracts an unexpected scheme (e.g. 'gopher://127.0.0.1:70').","commonSituations":"Users configuring api_base or a user-supplied URL field (e.g. file fetch, media URL, MCP/OAuth callback) with a non-HTTP scheme; accidentally including a Windows drive letter ('C:\\path') that urlparse reads as scheme 'c'; copy-paste of URLs with custom schemes like 'unix://' or 'ws://'.","solutions":["Change the URL to use http:// or https://.","If the scheme came from user input, validate/normalize it before passing it into litellm (urlparse(url).scheme in {'http','https'}).","For WebSocket endpoints, check whether the integration expects the http(s) upgrade form rather than ws://.","Strip or quote a leading path like 'C:\\...' that is being misparsed as a scheme."],"exampleFix":"# before\nresp = safe_get(client, \"ftp://files.example.com/data.csv\")\n\n# after\nresp = safe_get(client, \"https://files.example.com/data.csv\")","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef is_http_url(url: str) -> bool:\n    return urlparse(url).scheme in {\"http\", \"https\"} and bool(urlparse(url).hostname)","typeGuard":"def is_http_url(url) -> bool:\n    try:\n        p = urlparse(url)\n    except Exception:\n        return False\n    return p.scheme in {\"http\", \"https\"}","tryCatchPattern":"from litellm.litellm_core_utils.url_utils import SSRFError\n\ntry:\n    resp = safe_get(client, url)\nexcept SSRFError as e:\n    return bad_request(f\"URL rejected: {e}\")","preventionTips":["Validate scheme at input boundaries with urlparse before handing URLs to litellm.","Reject ws://, ftp://, file:// and bare hostnames in user URL fields.","Return 400s for scheme violations so users can self-correct."],"tags":["ssrf","url","scheme","security","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}