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_INET

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Verify the hostname resolves: `python -c 'import socket; print(socket.getaddrinfo("host", 80))'`.
  2. Retry transient DNS failures with exponential backoff (treat as retryable OSError).
  3. Switch resolver: TCPConnector(resolver=ThreadedResolver()) to use the OS getaddrinfo path, which may behave differently.
  4. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/5e7ef0622a85ff9f.json. Report an issue: GitHub.