The Fix
Fixes the issue where the TCPConnector does not use the session's event loop, which raises exceptions when an explicit loop is passed to ClientSession.
Based on closed aio-libs/aiohttp issue #11147 · PR/commit linked
Production note: Most teams hit this during upgrades or environment changes. Roll out with a canary and smoke critical endpoints (health, OpenAPI/docs) before 100%.
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+Added ``ssl_shutdown_timeout`` parameter to :py:class:`~aiohttp.ClientSession` and :py:class:`~aiohttp.TCPConnector` to control the grace period for SSL shutdown handshake on TLS connections. This helps prevent "connection reset" errors on the server side while avoiding excessive delays during connector cleanup. Note: This parameter only takes effect on Python 3.11+ -- by :user:`bdraco`.
diff --git a/CHANGES/11094.feature.rst b/CHANGES/11094.feature.rst
new file mode 120000
from asyncio import new_event_loop
from aiohttp import ClientSession
async def test():
other_loop = new_event_loop()
_ = ClientSession(loop=other_loop)
loop = new_event_loop()
loop.run_until_complete(test())
loop.close()
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Apply the official fix\nFixes the issue where the TCPConnector does not use the session's event loop, which raises exceptions when an explicit loop is passed to ClientSession.\nWhen NOT to use: This fix is not applicable if the application relies on the previous behavior of ClientSession.\n\n
Why This Fix Works in Production
- Trigger: loop.run_until_complete(test())
- Mechanism: The loop parameter of ClientSession is not passed to the TCPConnector, causing runtime exceptions
- If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.
Why This Breaks in Prod
- The loop parameter of ClientSession is not passed to the TCPConnector, causing runtime exceptions
- Surfaces as: Traceback (most recent call last):
Proof / Evidence
- GitHub issue: #11147
- Fix PR: https://github.com/aio-libs/aiohttp/pull/11095
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.70
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.39
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“The loop=loop argument was removed in https://github.com/aio-libs/aiohttp/pull/11095.”
“We removed it on master a long time ago. Looks like it didn't get preserved in the backport so we need to do a pr…”
Failure Signature (Search String)
- loop.run_until_complete(test())
Error Message
Stack trace
Error Message
-------------
Traceback (most recent call last):
File "C:\Users\***\Documents\PyCharm\Test\test.py", line 12, in <module>
loop.run_until_complete(test())
File "C:\Program Files\Python312\Lib\asyncio\base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "C:\Users\***\Documents\PyCharm\Test\test.py", line 8, in test
_ = ClientSession(loop=other_loop)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\***\.virtualenvs\Test-hO5JO9V5\Lib\site-packages\aiohttp\client.py", line 368, in __init__
raise RuntimeError("Session and connector has to use same event loop")
RuntimeError: Session and connector has to use same event loop
Stack trace
Error Message
-------------
Traceback (most recent call last):
File "C:\Users\***\Documents\PyCharm\Test\test.py", line 7, in <module>
session = ClientSession(loop=loop)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\***\.virtualenvs\Test-hO5JO9V5\Lib\site-packages\aiohttp\client.py", line 365, in __init__
connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\***\.virtualenvs\Test-hO5JO9V5\Lib\site-packages\aiohttp\connector.py", line 918, in __init__
super().__init__(
File "C:\Users\***\.virtualenvs\Test-hO5JO9V5\Lib\site-packages\aiohttp\connector.py", line 288, in __init__
loop = loop or asyncio.get_running_loop()
^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: no running event loop
Minimal Reproduction
from asyncio import new_event_loop
from aiohttp import ClientSession
async def test():
other_loop = new_event_loop()
_ = ClientSession(loop=other_loop)
loop = new_event_loop()
loop.run_until_complete(test())
loop.close()
What Broke
Creating ClientSession with a different loop leads to RuntimeError exceptions in production.
Why It Broke
The loop parameter of ClientSession is not passed to the TCPConnector, causing runtime exceptions
Fix Options (Details)
Option A — Apply the official fix
Fixes the issue where the TCPConnector does not use the session's event loop, which raises exceptions when an explicit loop is passed to ClientSession.
Fix reference: https://github.com/aio-libs/aiohttp/pull/11095
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not applicable if the application relies on the previous behavior of ClientSession.
Verify Fix
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
- Add a TLS smoke test that performs a real handshake in CI (include CA bundle validation and hostname checks).
- Alert on handshake failures by error string and endpoint to catch cert/CA changes quickly.
- 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.