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

  1. Verify the hostname exists: nslookup/dig <host> from the same host.
  2. Check /etc/resolv.conf (or container DNS config) points at a reachable nameserver.
  3. 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

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

Related errors


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_INET

View on GitHub (pinned to d041d4d0fd)