BerriAI/litellm · error · SSRFError

DNS resolution failed for

Error message

DNS resolution failed for '{hostname}': {e}

What it means

SSRFError raised when socket.getaddrinfo fails for the URL's hostname during SSRF validation; the underlying socket.gaierror is embedded in the message. This is an environmental DNS failure (name does not exist, resolver unreachable), not a policy decision — it happens before any address can be checked against the block list.

Solutions

  1. From the proxy host, verify resolution: getent hosts <hostname> or dig <hostname>.
  2. Fix the hostname typo or point the URL at a resolvable name.
  3. For internal names, ensure the proxy's resolver can find them (DNS entry, search suffix, or hosts file).
  4. Catch SSRFError and surface a 'could not resolve host' 4xx/502 to the caller instead of a 500.

Example fix

# before
resp = safe_get(client, user_url)  # unhandled SSRFError -> 500

# after
try:
    resp = safe_get(client, user_url)
except SSRFError as e:
    if "DNS resolution failed" in str(e):
        raise HTTPException(status_code=400, detail="Could not resolve host in URL")
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import socket

def host_resolves(url: str) -> bool:
    from urllib.parse import urlparse
    host = urlparse(url).hostname
    if not host:
        return False
    try:
        socket.getaddrinfo(host, 443, proto=socket.IPPROTO_TCP)
        return True
    except socket.gaierror:
        return False

if not host_resolves(user_url):
    raise HTTPException(status_code=400, detail="URL host does not resolve")

Try / catch

from litellm.litellm_core_utils.url_utils import SSRFError

try:
    resp = safe_get(client, user_url)
except SSRFError as e:
    if "DNS resolution failed" in str(e):
        raise HTTPException(status_code=400, detail="Could not resolve host in URL")
    raise

Prevention

When it happens

Trigger: Fetching a user URL whose host is typo'd or nonexistent (NXDOMAIN), a resolver outage in the container, an air-gapped or restricted network, or an internal hostname (.local, split-horizon name) the proxy's resolver cannot resolve.

Common situations: Self-hosted proxy deployments where the proxy pod uses different DNS than dev machines; expired domains in stored URLs; Kubernetes setups missing the internal DNS suffix.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/d44870ca28fa678b. Report an issue: GitHub.

Appendix: source

Thrown at litellm/litellm_core_utils/url_utils.py:282

    if parsed.scheme not in _ALLOWED_SCHEMES:
        raise SSRFError(f"URL scheme '{parsed.scheme}' is not allowed")

    hostname: Final = parsed.hostname
    if not hostname:
        raise SSRFError("URL has no hostname")

    port: Final = parsed.port
    default_port: Final = _default_port_for_scheme(parsed.scheme)
    effective_port: Final = port if port is not None else default_port
    host_header: Final = _format_host_header(hostname, effective_port, default_port)

    is_allowlisted: Final = _is_host_allowlisted(hostname, effective_port)

    # Resolve hostname and validate ALL addresses
    try:
        addrinfo: Final = socket.getaddrinfo(hostname, effective_port, proto=socket.IPPROTO_TCP)
    except socket.gaierror as e:
        raise SSRFError(f"DNS resolution failed for '{hostname}': {e}")

    if not addrinfo:
        raise SSRFError(f"No addresses found for '{hostname}'")

    if not is_allowlisted:
        for family, type_, proto, canonname, sockaddr in addrinfo:
            resolved_ip = _sockaddr_host(sockaddr)
            if _is_blocked_ip(resolved_ip):
                raise SSRFError(
                    f"URL targets a blocked address ({resolved_ip}). "
                    "If this is a legitimate internal service, add the host "
                    "to `user_url_allowed_hosts` in general_settings."
                )

    # For HTTPS with SSL verification enabled, TLS certificate validation
    # binds the connection to the hostname — DNS rebinding can't redirect
    # to a different server because the cert wouldn't match.
    # When SSL verification is disabled, this defense doesn't apply, so

View on GitHub (pinned to 77b7c6c40c)