aio-libs/aiohttp · error · OSError
DNS lookup failed
Error message
DNS lookup failed
What it means
Raised by AsyncResolver.resolve after aiodns.error.DNSError occurs during getaddrinfo. aiohttp translates the aiodns exception into OSError(None, msg) where msg is the aiodns error description (or 'DNS lookup failed' as fallback). This surfaces as a network-layer failure on the connector. Common aiodns codes include ARES_ENOTFOUND (no such host) and ARES_ESERVFAIL (server failure).
Source
Thrown at aiohttp/resolver.py:145
host,
port=port,
type=socket.SOCK_STREAM,
family=family,
flags=_AI_ADDRCONFIG,
)
except aiodns.error.DNSError:
if not _is_windows_localhost(host):
raise
resp = await self._resolver.getaddrinfo(
host,
port=port,
type=socket.SOCK_STREAM,
family=family,
flags=0,
)
except aiodns.error.DNSError as exc:
msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
raise OSError(None, msg) from exc
hosts: list[ResolveResult] = []
for node in resp.nodes:
address: tuple[bytes, int] | tuple[bytes, int, int, int] = node.addr
if node.family == socket.AF_INET6:
if len(address) > 3 and address[3]:
# This is essential for link-local IPv6 addresses.
# LL IPv6 is a VERY rare case. Strictly speaking, we should use
# getnameinfo() unconditionally, but performance makes sense.
result = await self._resolver.getnameinfo(
(address[0].decode("ascii"), *address[1:]),
_NAME_SOCKET_FLAGS,
)
resolved_host = result.node
else:
resolved_host = address[0].decode("ascii")
port = address[1]
else: # IPv4
assert node.family == socket.AF_INETView on GitHub (pinned to c0ef574e29)
Solutions
- Verify the hostname resolves: `python -c 'import socket; print(socket.getaddrinfo("host", 80))'`.
- Retry transient DNS failures with exponential backoff (treat as retryable OSError).
- Switch resolver: TCPConnector(resolver=ThreadedResolver()) to use the OS getaddrinfo path, which may behave differently.
- If behind a custom DNS, point aiodns at it: AsyncResolver(nameservers=['10.0.0.53'], timeout=5).
Example fix
// before
async with aiohttp.ClientSession(connector=TCPConnector(resolver=AsyncResolver())) as s:
await s.get('http://does-not-exist.invalid/')
// after
try:
async with s.get(url) as r:
...
except OSError as e: # covers DNS lookup failed
log.warning('dns failure for %s: %s', url, e)
await asyncio.sleep(backoff); retry() Defensive patterns
Strategy: retry
Try / catch
async def fetch(session, url, attempts=3):
for i in range(attempts):
try:
async with session.get(url) as r:
return await r.read()
except OSError as e:
# 'DNS lookup failed' and friends
if i == attempts - 1:
raise
await asyncio.sleep(2 ** i) Prevention
- Treat OSError from resolve as retryable.
- Log exc.args[1] to distinguish NXDOMAIN vs SERVFAIL.
- Configure aiodns nameservers/timeout explicitly in production.
When it happens
Trigger: Calling client.get('http://nonexistent.invalid/') while using AsyncResolver; DNS server unreachable or returning SERVFAIL/REFUSED; the hostname does not resolve (NXDOMAIN); transient DNS outage during request.
Common situations: Wrong hostname in config; DNS not ready in a freshly started container (e.g. k8s service not yet registered); restrictive DNS that blocks the domain; flaky upstream resolver; IPv6-only resolution failing under AF_INET family constraint.
Related errors
- Connection timeout to host {url}
- either both host and port or none of them are allowed
- Connector is closed
- Cannot write to closing transport
- Resolver requires aiodns library
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/5e7ef0622a85ff9f.json.
Report an issue: GitHub.