langchain-ai/langchain · error · SSRFBlockedError
DNS resolution failed
Error message
DNS resolution failed
What it means
The async SSRF-guard httpx transport (`libs/core/langchain_core/_security/_transport.py`, `handle_async_request`) resolves the request hostname via `socket.getaddrinfo` before connecting; a `socket.gaierror` becomes `SSRFBlockedError('DNS resolution failed')`. DNS failure is treated as a block so the request never proceeds with an unvalidated destination.
Source
Thrown at libs/core/langchain_core/_security/_transport.py:84
validate_url_sync(str(request.url), self._policy)
# Allowed-hosts bypass - skip DNS/IP validation entirely.
allowed = {h.lower() for h in _effective_allowed_hosts(self._policy)}
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.View on GitHub (pinned to e32fa9a52e)
Solutions
- Confirm the hostname resolves from the same runtime: `python -c "import socket; socket.getaddrinfo('host', 443)"`
- Fix or fully qualify the target hostname / use an IP literal if policy allows
- Repair container DNS configuration (resolv.conf, k8s CoreDNS, docker --dns)
- If the host is a trusted internal allow-listed name, add it to the transport's allowed-hostnames set so it bypasses resolution-based checks
Defensive patterns
Strategy: try-catch
Validate before calling
import socket
def resolves(host: str, port: int) -> bool:
try:
return bool(socket.getaddrinfo(host, port, type=socket.SOCK_STREAM))
except socket.gaierror:
return False Try / catch
try:
resp = await client.get(url)
except SSRFBlockedError as e:
if 'DNS resolution failed' in str(e):
logger.warning('unresolvable target %s', url)
return None
raise Prevention
- Validate external hostnames at configuration load time
- Keep an allow-list for trusted internal hosts so they bypass DNS validation
- In tests, monkeypatch getaddrinfo with realistic addrinfo tuples
When it happens
Trigger: Issuing a request through an httpx client configured with the SSRF-guard async transport where the URL hostname does not resolve (NXDOMAIN, SERVFAIL, no resolver). Affects GETs to mistyped domains or hostnames only resolvable inside another network.
Common situations: Offline CI; containers with broken DNS; internal hostnames in one environment used in another; testing SSRF protection itself with dead hostnames.
Understand the failure class
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
Related errors
- DNS resolution returned no results
- DNS resolution failed
- Failed to resolve hostname '{hostname}': {e}
- Network error while validating URL: {e}
- invalid IP address
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/22131a5707aadfbb.
Report an issue: GitHub.