langchain-ai/langchain · error · SSRFBlockedError
DNS resolution returned no results
Error message
DNS resolution returned no results
What it means
After a successful `getaddrinfo` call, the async SSRF transport requires a non-empty address list; an empty result raises `SSRFBlockedError('DNS resolution returned no results')`. This is a defensive branch — `getaddrinfo` normally raises rather than returning empty — and indicates a resolver behaving oddly.
Source
Thrown at libs/core/langchain_core/_security/_transport.py:88
if hostname.lower() in allowed:
return await self._inner.handle_async_request(request)
# 4. DNS resolution
port = request.url.port or (443 if scheme == "https" else 80)
try:
addrinfo = await asyncio.to_thread(
socket.getaddrinfo,
hostname,
port,
type=socket.SOCK_STREAM,
)
except socket.gaierror as exc:
msg = "DNS resolution failed"
raise SSRFBlockedError(msg) from exc
if not addrinfo:
msg = "DNS resolution returned no results"
raise SSRFBlockedError(msg)
# 5. Validate ALL resolved IPs - any blocked means reject.
for _family, _type, _proto, _canonname, sockaddr in addrinfo:
ip_str: str = sockaddr[0] # type: ignore[assignment]
validate_resolved_ip(ip_str, self._policy)
# 6. Pin to first resolved IP.
pinned_ip = addrinfo[0][4][0]
# 7. Rewrite URL to use pinned IP, preserving Host header and SNI.
pinned_url = request.url.copy_with(host=pinned_ip)
# Build extensions dict, adding sni_hostname for HTTPS so TLS
# certificate validation uses the original hostname.
extensions = dict(request.extensions)
if scheme == "https":
extensions["sni_hostname"] = hostname.encode("ascii")
View on GitHub (pinned to e32fa9a52e)
Solutions
- If you mock or wrap `socket.getaddrinfo` in tests, return a realistic non-empty addrinfo list
- Check `getent hosts <hostname>` / `socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)` in the failing environment to see what the resolver actually returns
- Eliminate custom resolver shims (LD_PRELOAD, nsswitch overrides) or fix them to return addresses
- Retry in case of transient resolver weirdness; escalate to infrastructure if persistent
Defensive patterns
Strategy: try-catch
Validate before calling
import socket
def resolver_healthy(host: str, port: int) -> bool:
try:
return len(socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)) > 0
except OSError:
return False Try / catch
try:
resp = await client.get(url)
except SSRFBlockedError as e:
if 'returned no results' in str(e):
# resolver behaved abnormally; log and surface infra issue
logger.error('resolver returned empty for %s', url)
raise Prevention
- Never mock getaddrinfo with [] in tests — return realistic tuples
- Avoid custom resolver shims unless they preserve the standard contract
- Watch for this message as a canary for broken resolver wrappers
When it happens
Trigger: An exotic resolver/mocked `getaddrinfo` returning `[]`; unusual platform resolver behavior (some sandbox wrappers, glibc edge cases, or monkeypatched sockets in tests) yielding no addresses for the port/type combination.
Common situations: Test suites that patch `socket.getaddrinfo` incompletely; custom DNS shims or LD_PRELOAD resolvers; rare Nsswitch/mDNS configurations.
Understand the failure class
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
Related errors
- DNS resolution failed
- Kubernetes internal DNS
- DNS resolution failed
- Failed to resolve hostname '{hostname}': {e}
- Network error while validating URL: {e}
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/4c5034dc2d83e8ce.
Report an issue: GitHub.