util.make_aiohttp_session: wrap aiohttp-socks 0.11+ excs to ClientError
What changed, and why it matters
This commit fixes a compatibility issue with newer versions of a proxy library used by Electrum. The newer library changed which error types it raises when proxy connections fail. Because Electrum's code was written to catch the old error types, unexpected proxy errors could slip through, potentially causing crashes or confusing error messages instead of being handled gracefully. The fix converts the new proxy errors into a standard network error type that Electrum already knows how to handle.
Treat as a robustness/compatibility fix rather than an active vulnerability. Users relying on SOCKS/Tor proxies should upgrade to ensure proxy failures are handled gracefully. Review other call sites that catch OSError/TimeoutError to confirm no similar mismatches remain.
Security signals we found
Exception-handling mismatch after dependency update
Proxy connection errors could propagate uncaught
Potential denial-of-service via unhandled exception causing task/coroutine termination
No input validation or memory safety issue
Evidence from the diff
The patch updates Electrum to handle exception-class changes in aiohttp-socks/python-socks 0.11+. Previously, proxy errors inherited from OSError/TimeoutError; now they inherit from Exception. Electrum’s existing exception handlers often catch OSError, TimeoutError, or aiohttp.ClientError. The fix wraps make_aiohttp_session in an async context manager that catches ProxyConnectionError, ProxyTimeoutError, and ProxyError and re-raises them as aiohttp.ClientError. It also broadens one lnworker.py handler from ClientConnectorError to ClientError. This restores graceful error handling for proxy connection failures.
Changed components
electrum/util.py: make_aiohttp_session / _make_aiohttp_sessionelectrum/lnworker.py: watchtower connection error handlingInspect captured patch +21 / −2
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
index 6e65cf6..874c749 100644
--- a/electrum/lnworker.py
+++ b/electrum/lnworker.py
@@ -1184,7 +1184,7 @@ class LNWallet(Logger):
watchtower.add_method('add_sweep_tx')
for chan in self.channels.values():
await self.sync_channel_with_watchtower(chan, watchtower)
- except aiohttp.client_exceptions.ClientConnectorError:
+ except aiohttp.ClientError:
self.logger.info(f'could not contact remote watchtower {watchtower_url}')
def get_watchtower_ctn(self, channel_point):
diff --git a/electrum/util.py b/electrum/util.py
index bc870ea..a7bad5c 100644
--- a/electrum/util.py
+++ b/electrum/util.py
@@ -55,6 +55,7 @@ import functools
from functools import partial
from abc import abstractmethod, ABC
import enum
+import contextlib
from contextlib import nullcontext, suppress
import traceback
import inspect
@@ -62,6 +63,7 @@ import weakref
import aiohttp
from aiohttp_socks import ProxyConnector, ProxyType
+from aiohttp_socks import ProxyConnectionError, ProxyTimeoutError, ProxyError
import aiorpcx
import certifi
import dns.asyncresolver
@@ -1354,7 +1356,7 @@ def make_aiohttp_proxy_connector(proxy: 'ProxySettings', ssl_context: Optional[s
)
-def make_aiohttp_session(proxy: Optional['ProxySettings'], headers=None, timeout=None):
+def _make_aiohttp_session(proxy: Optional['ProxySettings'], headers=None, timeout=None):
if headers is None:
headers = {'User-Agent': 'Electrum'}
if timeout is None:
@@ -1373,6 +1375,23 @@ def make_aiohttp_session(proxy: Optional['ProxySettings'], headers=None, timeout
return aiohttp.ClientSession(headers=headers, timeout=timeout, connector=connector)
+@contextlib.asynccontextmanager
+async def make_aiohttp_session(proxy: Optional['ProxySettings'], headers=None, timeout=None):
+ """
+ Caller should typically handle at least:
+ - aiohttp.ClientError
+ - asyncio.TimeoutError
+ """
+ try:
+ async with _make_aiohttp_session(proxy, headers=headers, timeout=timeout) as session:
+ yield session
+ except (ProxyConnectionError, ProxyTimeoutError, ProxyError) as e:
+ # We unify all proxy-related exceptions to a single type.
+ # Maybe it would be better to unify to ProxyError, but ~all call sites already expect aiohttp.ClientError,
+ # and that is a generic http-related error that we usually display the str() of, so let's just reuse that.
+ raise aiohttp.ClientError(f"proxy error: {repr(e)}") from e
+
+
class OldTaskGroup(aiorpcx.TaskGroup):
"""Automatically raises exceptions on join; as in aiorpcx prior to version 0.20.
That is, when using TaskGroup as a context manager, if any task encounters an exception,
Why this scored 37/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.