BerriAI/litellm · error · SSRFError
getaddrinfo returned non-string host: {host!r}
Error message
getaddrinfo returned non-string host: {host!r} What it means
Raised by _sockaddr_host during litellm's SSRF validation when a getaddrinfo result's sockaddr does not start with a string host. mypy types sockaddr[0] as str | int because some address families carry ints, and this boundary check narrows the type; a non-string would mean there is no IP to check against the SSRF blocklist, so it fails closed with SSRFError. In practice this should never fire with CPython's IPPROTO_TCP getaddrinfo — it guards against stdlib changes or exotic platforms.
Source
Thrown at litellm/litellm_core_utils/url_utils.py:217
bracketed: Final = f"[{hostname}]" if ":" in hostname else hostname
if port == default_port:
return bracketed
return f"{bracketed}:{port}"
def _sockaddr_host(sockaddr: Any) -> str:
"""Return the host element of a ``getaddrinfo`` sockaddr as ``str``.
``getaddrinfo`` with ``IPPROTO_TCP`` returns AF_INET / AF_INET6 sockaddrs
whose first element is always a host string. mypy types it as
``str | int`` (since sockaddrs for other families can hold ints), so we
narrow at the boundary. Fail closed if the stdlib ever returns something
unexpected — a non-string here would mean we have no IP to check against
the SSRF blocklist.
"""
host: Final = sockaddr[0]
if not isinstance(host, str):
raise SSRFError(f"getaddrinfo returned non-string host: {host!r}")
return host
def _is_host_allowlisted(hostname: str, effective_port: int) -> bool:
"""Check whether a host is in the admin-configured allowlist.
Admin entries may be ``hostname`` (any port) or ``hostname:port``. IPv6
literals are written bracketed (``[::1]`` / ``[::1]:8080``). Matching
is case-insensitive on the hostname.
"""
configured: Final[list[str]] = getattr(litellm, "user_url_allowed_hosts", []) or []
if not configured:
return False
normalized_host: Final = _normalize_host(hostname)
host_repr: Final = f"[{normalized_host}]" if ":" in normalized_host else normalized_host
candidates: Final[set[str]] = {host_repr, f"{host_repr}:{effective_port}"}
allowlist: Final[set[str]] = {_normalize_host(entry) for entry in configured if entry}
return bool(candidates & allowlist)View on GitHub (pinned to 6c2dcb801b)
Solutions
- If you see this in tests, fix the mocked getaddrinfo to return proper (family, type, proto, canonname, (host, port)) tuples for AF_INET/AF_INET6.
- If it occurs in production, inspect the resolver environment (custom resolvers, LD_PRELOAD, patched socket) — stock CPython cannot produce this.
- Report it upstream to litellm with the platform and Python version, since it indicates stdlib behavior outside the documented contract.
Example fix
# before (broken test mock)
sock.getaddrinfo = lambda *a, **k: [(2, 1, 6, '', (80, 1))] # port first, int host
# after
sock.getaddrinfo = lambda *a, **k: [(2, 1, 6, '', ('93.184.216.34', 80))] Defensive patterns
Strategy: try-catch
Type guard
def is_inet_sockaddr(sockaddr) -> bool:
return isinstance(sockaddr, tuple) and len(sockaddr) >= 1 and isinstance(sockaddr[0], str) Try / catch
from litellm.litellm_core_utils.url_utils import SSRFError
try:
validate_url(url)
except SSRFError:
raise # surface to caller Prevention
- In tests, mock getaddrinfo with well-formed AF_INET/AF_INET6 sockaddr tuples (host string first).
- Avoid monkeypatching socket.getaddrinfo in production code paths.
- Treat this error as an environment/mock bug signal, not a user-input problem.
When it happens
Trigger: validate_url() on a user-supplied URL whose DNS resolution returns an AF family whose sockaddr's first element is an int (non-INET/INET6 family from a patched or non-standard resolver). Essentially unreachable on stock CPython; realistic only with monkeypatched socket.getaddrinfo, exotic AI_* families, or a platform quirk.
Common situations: Test suites that mock/monkeypatch socket.getaddrinfo with malformed sockaddr tuples; running under alternative Python implementations or LD_PRELOAD'd resolvers; bugs in custom DNS shims injected into the process.
Understand the failure class
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
Related errors
- No addresses found for '{hostname}'
- DNS resolution failed for '{hostname}': {e}
- Error listing files in '{directory_path}': {e}
- MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. htt
- Mavvrik FOCUS destination: {label} must be a GCS endpoint (s
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/1a5002899fc48f0a.
Report an issue: GitHub.