Jump to solution
Verify

The Fix

Addresses issue #11091 by adding a configurable `ssl_shutdown_timeout` parameter to control the SSL shutdown handshake timeout, fixing the 30-second blocking issue when closing SSL connections.

Based on closed aio-libs/aiohttp issue #11091 · 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%.

Jump to Verify Open PR/Commit
@@ -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
repro.py
import asyncio import logging from azure.data.tables.aio import TableServiceClient logger = logging.getLogger(__name__) async def create_table_with_async_table_service_client() -> None: logger.info("Creating table asynchronously...") async with TableServiceClient.from_connection_string("<connection_string>") as table_service_client: await table_service_client.create_table_if_not_exists("TestTable") logger.info("TestTable created successfully or already exists") logger.info("AsyncTableServiceClient exited successfully") async def do_nothing_in_async_table_service_client() -> None: logger.info("Doing nothing in AsyncTableServiceClient...") async with AsyncTableServiceClient.from_connection_string("<connection_string>"): logger.info("AsyncTableServiceClient is ready for use - doing nothing") logger.info("AsyncTableServiceClient exited successfully") async def main() -> None: logging.basicConfig(level=logging.INFO) await create_table_with_async_table_service_client() await do_nothing_in_async_table_service_client() if __name__ == "__main__": 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\nAddresses issue #11091 by adding a configurable `ssl_shutdown_timeout` parameter to control the SSL shutdown handshake timeout, fixing the 30-second blocking issue when closing SSL connections.\nWhen NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.\n\n

Why This Fix Works in Production

  • Trigger: DEBUG:asyncio:Using selector: EpollSelector
  • Mechanism: The SSL shutdown handshake can timeout when the remote server doesn't respond, causing delays
Production impact:
  • 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 SSL shutdown handshake can timeout when the remote server doesn't respond, causing delays
  • Surfaces as: DEBUG:asyncio:Using selector: EpollSelector

Proof / Evidence

Discussion

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

“We completely understand that this can cause problems in real-world scenarios”
@bdraco · 2025-05-30 · confirmation · source
“Repro: Stolen from here: https://github.com/Azure/azure-sdk-for-python/issues/41363 Maybe some folks from here can pile onto the azure issue.”
@jabbera · 2025-05-30 · repro detail · source
“Asking the maintainers of those projects that misbehave might help, since those people would know better how to distill a reproducer from their side to…”
@webknjaz · 2025-05-30 · repro detail · source
“~~Thanks for sharing the repro”
@bdraco · 2025-05-30 · repro detail · source

Failure Signature (Search String)

  • DEBUG:asyncio:Using selector: EpollSelector

Error Message

Stack trace
error.txt
Error Message ------------- DEBUG:asyncio:Using selector: EpollSelector Status: 200 Streaming response: ERROR:root:Error while closing connector: ClientConnectionError('Connection lost: SSL shutdown timed out')

Minimal Reproduction

repro.py
import asyncio import logging from azure.data.tables.aio import TableServiceClient logger = logging.getLogger(__name__) async def create_table_with_async_table_service_client() -> None: logger.info("Creating table asynchronously...") async with TableServiceClient.from_connection_string("<connection_string>") as table_service_client: await table_service_client.create_table_if_not_exists("TestTable") logger.info("TestTable created successfully or already exists") logger.info("AsyncTableServiceClient exited successfully") async def do_nothing_in_async_table_service_client() -> None: logger.info("Doing nothing in AsyncTableServiceClient...") async with AsyncTableServiceClient.from_connection_string("<connection_string>"): logger.info("AsyncTableServiceClient is ready for use - doing nothing") logger.info("AsyncTableServiceClient exited successfully") async def main() -> None: logging.basicConfig(level=logging.INFO) await create_table_with_async_table_service_client() await do_nothing_in_async_table_service_client() if __name__ == "__main__": asyncio.run(main())

What Broke

Users experience a 30-second delay when closing SSL connections to streaming endpoints.

Why It Broke

The SSL shutdown handshake can timeout when the remote server doesn't respond, causing delays

Fix Options (Details)

Option A — Apply the official fix

Addresses issue #11091 by adding a configurable `ssl_shutdown_timeout` parameter to control the SSL shutdown handshake timeout, fixing the 30-second blocking issue when closing SSL connections.

When NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.

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

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

  • Do not use if it changes public behavior or if the failure cannot be reproduced.

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

  • Track RSS + object counts after deployments; alert on monotonic growth and GC pressure.
  • Add a long-running test that repeats the failing call path and asserts stable memory.
  • 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.