From ba9b33e63a18841cdb7cf5f912d99ccbea91dee4 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Thu, 10 Oct 2024 09:35:22 -0500
Subject: [PATCH] [PR #9451/216e082 backport][3.10] Fix AsyncResolver
 swallowing the error message (#9452)

---
 CHANGES/9451.bugfix.rst |  1 +
 aiohttp/resolver.py     |  4 ++--
 tests/test_resolver.py  | 30 +++++++++++++++++++++++++++++-
 3 files changed, 32 insertions(+), 3 deletions(-)
 create mode 100644 CHANGES/9451.bugfix.rst

diff --git a/CHANGES/9451.bugfix.rst b/CHANGES/9451.bugfix.rst
new file mode 100644
index 00000000000..2adcbc66273
--- /dev/null
+++ b/CHANGES/9451.bugfix.rst
@@ -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 6283ec2b8d5..385ae21abf5 100644
--- a/aiohttp/resolver.py
+++ b/aiohttp/resolver.py
@@ -111,7 +111,7 @@ async def resolve(
             )
         except aiodns.error.DNSError as exc:
             msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
-            raise OSError(msg) from exc
+            raise OSError(None, msg) from exc
         hosts: List[ResolveResult] = []
         for node in resp.nodes:
             address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr
@@ -145,7 +145,7 @@ async def resolve(
             )
 
         if not hosts:
-            raise OSError("DNS lookup failed")
+            raise OSError(None, "DNS lookup failed")
 
         return hosts
 
diff --git a/tests/test_resolver.py b/tests/test_resolver.py
index 8b2ea620037..6322bcfca64 100644
--- a/tests/test_resolver.py
+++ b/tests/test_resolver.py
@@ -339,7 +339,35 @@ async def test_async_resolver_query_ipv6_positive_lookup(loop) -> None:
         mock().query.assert_called_with("www.python.org", "AAAA")
 
 
-async def test_async_resolver_aiodns_not_present(loop, monkeypatch) -> None:
+@pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
+async def test_async_resolver_error_messages_passed(
+    loop: asyncio.AbstractEventLoop,
+) -> None:
+    """Ensure error messages are passed through from aiodns."""
+    with patch("aiodns.DNSResolver", autospec=True, spec_set=True) as mock:
+        mock().getaddrinfo.side_effect = aiodns.error.DNSError(1, "Test error message")
+        resolver = AsyncResolver()
+        with pytest.raises(OSError, match="Test error message") as excinfo:
+            await resolver.resolve("x.org")
+
+        assert excinfo.value.strerror == "Test error message"
+
+
+@pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
+async def test_async_resolver_error_messages_passed_no_hosts(
+    loop: asyncio.AbstractEventLoop,
+) -> None:
+    """Ensure error messages are passed through from aiodns."""
+    with patch("aiodns.DNSResolver", autospec=True, spec_set=True) as mock:
+        mock().getaddrinfo.return_value = fake_aiodns_getaddrinfo_ipv6_result([])
+        resolver = AsyncResolver()
+        with pytest.raises(OSError, match="DNS lookup failed") as excinfo:
+            await resolver.resolve("x.org")
+
+        assert excinfo.value.strerror == "DNS lookup failed"
+
+
+async def test_async_resolver_aiodns_not_present(loop: Any, monkeypatch: Any) -> None:
     monkeypatch.setattr("aiohttp.resolver.aiodns", None)
     with pytest.raises(RuntimeError):
         AsyncResolver(loop=loop)