aio-libs/aiohttp · error · OSError
DNS lookup failed
Error message
DNS lookup failed
What it means
Raised by AsyncResolver.resolve when an aiodns DNSError occurs during getaddrinfo (or the windows-localhost getaddrinfo fallback). The DNSError is translated into an OSError whose message is taken from the underlying aiodns error; the literal 'DNS lookup failed' is only the fallback when the DNSError carries no args. This surfaces DNS protocol-level failures (NXDOMAIN, SERVFAIL, timeout) to the caller as a standard OSError.
Solutions
- Verify the hostname exists: nslookup/dig <host> from the same host.
- Check /etc/resolv.conf (or container DNS config) points at a reachable nameserver.
- Retry with backoff for transient failures, or configure a fallback resolver (e.g. resolver=AsyncResolver(nameservers=['8.8.8.8'])).
Example fix
# before
connector = TCPConnector(resolver=AsyncResolver())
# resolves bad-host.example -> OSError: DNS lookup failed
# after
try:
async with session.get('https://bad-host.example') as r:
...
except OSError as e:
log.warning('DNS failed for host: %s', e) Defensive patterns
Strategy: retry
Validate before calling
import socket
try:
socket.gethostbyname(host)
except socket.gaierror:
raise ValueError(f'hostname does not resolve: {host}') Type guard
import socket
def host_resolves(host: str) -> bool:
try:
socket.gethostbyname(host)
return True
except socket.gaierror:
return False Try / catch
for attempt in range(3):
try:
async with session.get(url) as r:
return await r.read()
except OSError as e:
# OSError covers DNS lookup failures from the resolver
last = e
raise last Prevention
- Validate hostnames before issuing requests in long-running services.
- Configure nameservers explicitly in restricted networks.
- Wrap outbound requests in retry-with-backoff for transient DNS failures.
When it happens
Trigger: Resolving a non-existent hostname (NXDOMAIN); a DNS server returning SERVFAIL; aiodns timing out on a dead resolver; network partition blocking port 53. The host was not the windows-localhost special case, so the exception propagates.
Common situations: Misconfigured resolv.conf; pointing at an internal DNS that lacks the record; transient ISP DNS outage; typo in the hostname; IPv6-only resolver unreachable from an IPv4-only host.
Understand the failure class
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
Related errors
- Resolver requires aiodns library
- Cannot connect to unix socket
- Connection closed.
- Connection lost
- Connection lost
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/5e7ef0622a85ff9f.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)