Jump to solution
Verify

The Fix

Fixes the issue of error messages being swallowed in AsyncResolver by correctly raising OSError with the appropriate arguments.

Based on closed aio-libs/aiohttp issue #9447 · PR/commit linked

Production note: Watch p95/p99 latency and retry volume; timeouts can turn into retry storms and duplicate side-effects.

Jump to Verify Open PR/Commit
@@ -0,0 +1 @@ @@ -0,0 +1 @@ +Fixed error messages from :py:class:`~aiohttp.resolver.AsyncResolver` being swallowed -- by :user:`bdraco`. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index daa90b828c6..1025df06398 100644
repro.py
import aiohttp import asyncio import datetime import logging async def fetch(client): timeout = aiohttp.ClientTimeout(total=10) async with client.head("https://checkonline.home-assistant.io/online.txt", timeout=timeout) as resp: assert resp.status == 200 return await resp.text() async def main(): async with aiohttp.ClientSession() as client: while True: print(datetime.datetime.now()) try: html = await fetch(client) print(html) except Exception as e: logging.exception(e) logging.basicConfig( format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S' ) asyncio.run(main())
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Apply the official fix\nFixes the issue of error messages being swallowed in AsyncResolver by correctly raising OSError with the appropriate arguments.\nWhen NOT to use: This fix should not be applied if the application relies on the previous error handling behavior.\n\n

Why This Fix Works in Production

  • Trigger: resp = await self._resolver.getaddrinfo(
  • Mechanism: The OSError was incorrectly raised without the appropriate errno, causing error messages to be swallowed
Production impact:
  • If left unfixed, tail latency can spike under load and surface as timeouts/retries (amplifying incident impact).

Why This Breaks in Prod

  • Shows up under Python 3.12 in real deployments (not just unit tests).
  • The OSError was incorrectly raised without the appropriate errno, causing error messages to be swallowed
  • Surfaces as: Traceback (most recent call last):

Proof / Evidence

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“the missing errors are fixed in https://github.com/aio-libs/aiohttp/pull/9451”
@bdraco · 2024-10-10 · confirmation · source
“Changing the host to xcheckonline.home-assistant.io is enough to show its always swallowing it”
@bdraco · 2024-10-10 · source
“It looks like strerror is never set from aiodns”
@bdraco · 2024-10-10 · source
“we raise OSError(msg) but the first arg to OSError is the errno So it should be OSError(None, msg)”
@bdraco · 2024-10-10 · source

Failure Signature (Search String)

  • resp = await self._resolver.getaddrinfo(

Error Message

Stack trace
error.txt
Error Message ------------- Traceback (most recent call last): File "/usr/local/lib/python3.12/site-packages/aiohttp/resolver.py", line 105, in resolve resp = await self._resolver.getaddrinfo( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aiodns.error.DNSError: (12, 'Timeout while contacting DNS servers') The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1294, in _create_direct_connection hosts = await self._resolve_host(host, port, traces=traces) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 945, in _resolve_host return await asyncio.shield(resolved_host_task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 982, in _resolve_host_with_throttle addrs = await self._resolver.resolve(host, port, family=self._family) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/aiohttp/resolver.py", line 114, in resolve raise OSError(msg) from exc OSError: Timeout while contacting DNS servers The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/tmp/test3.py", line 16, i ... (truncated) ...

Minimal Reproduction

repro.py
import aiohttp import asyncio import datetime import logging async def fetch(client): timeout = aiohttp.ClientTimeout(total=10) async with client.head("https://checkonline.home-assistant.io/online.txt", timeout=timeout) as resp: assert resp.status == 200 return await resp.text() async def main(): async with aiohttp.ClientSession() as client: while True: print(datetime.datetime.now()) try: html = await fetch(client) print(html) except Exception as e: logging.exception(e) logging.basicConfig( format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S' ) asyncio.run(main())

Environment

  • Python: 3.12

What Broke

Users experience silent failures when DNS resolution times out, leading to confusion and unhandled exceptions.

Why It Broke

The OSError was incorrectly raised without the appropriate errno, causing error messages to be swallowed

Fix Options (Details)

Option A — Apply the official fix

Fixes the issue of error messages being swallowed in AsyncResolver by correctly raising OSError with the appropriate arguments.

When NOT to use: This fix should not be applied if the application relies on the previous error handling behavior.

Fix reference: https://github.com/aio-libs/aiohttp/pull/9451

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • This fix should not be applied if the application relies on the previous error handling behavior.

Verify Fix

verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.

Did This Fix Work in Your Case?

Quick signal helps us prioritize which fixes to verify and improve.

Prevention

  • Make timeouts explicit and test them (unit + integration) to avoid silent behavior changes.
  • Instrument retries (attempt count + reason) and alert on spikes to catch dependency slowdowns.

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.