Jump to solution
Verify

The Fix

Fixes a hang when reusing a connection that was previously used for a streaming download and returned to the pool in paused state.

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

Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.

Jump to Verify Open PR/Commit
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@ +Fixed a hang where a connection previously used for a streaming +download could be returned to the pool in a paused state. +-- by :user:`javitonino`.
repro.py
import asyncio import aiohttp async def run(): for _ in range(10): async with aiohttp.ClientSession() as session: response = await session.get( "http://localhost:4566/bucket/ae4bbfcdc47f450aa8557abefeba4a5ct", ) i = 0 async for chunk in response.content.iter_chunked(1024): i += 1 print(f"chunk {i}") # This process only fails near the end of the download, this condition is just here to speed up the testing # by skipping some uploads but the bug reproduces without it, it just takes more time. if i >= 900: print("Streamed, time to upload") # It hangs awaiting this, no timeout is raised await session.put( "http://localhost:4566/bucket/output/some_file", data=b"" ) print("Uploaded") asyncio.run(run())
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 a hang when reusing a connection that was previously used for a streaming download and returned to the pool in paused state.\nWhen NOT to use: Do not use this fix if connection reuse is not a concern in your application.\n\n

Why This Fix Works in Production

  • Trigger: message, payload = await protocol.read() # type: ignore[union-attr]
  • Mechanism: The connection was returned to the pool in a paused state after a streaming download
Production impact:
  • If left unfixed, failures can be intermittent under concurrency (hard to reproduce; shows up as sporadic 5xx/timeouts).

Why This Breaks in Prod

  • Shows up under Python 3.12 in real deployments (not just unit tests).
  • The connection was returned to the pool in a paused state after a streaming download
  • Surfaces as: Traceback (most recent call last):

Proof / Evidence

Discussion

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

“> I think the problem is that when the connection is released to the pool (on download eof) while the iteration is still ongoing, the…”
@Dreamsorcerer · 2024-12-16 · confirmation · source
“Does it happen when using async with properly? I think I saw something similar recently that looked like some kind of race condition that wasn't…”
@Dreamsorcerer · 2024-12-16 · source
“Do you mean the request context manager like so: It still happens when using it like this.”
@javitonino · 2024-12-16 · source
“Right, that's not related then to this issue.”
@Dreamsorcerer · 2024-12-16 · source

Failure Signature (Search String)

  • message, payload = await protocol.read() # type: ignore[union-attr]

Error Message

Stack trace
error.txt
Error Message ------------- Traceback (most recent call last): File "venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 1055, in start message, payload = await protocol.read() # type: ignore[union-attr] ^^^^^^^^^^^^^^^^^^^^^ File "venv/lib/python3.12/site-packages/aiohttp/streams.py", line 668, in read await self._waiter asyncio.exceptions.CancelledError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "replicate.py", line 24, in <module> asyncio.run(run()) File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "replicate.py", line 18, in run await session.put( File "venv/lib/python3.12/site-packages/aiohttp/client.py", line 728, in _request await resp.start(conn) File "venv/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 1050, in start with self._timer: ^^^^^^^^^^^ File "venv/lib/python3.12/site-packages/aiohttp/helpers.py", line 671, in __exit__ raise asyncio.TimeoutError from ... (truncated) ...

Minimal Reproduction

repro.py
import asyncio import aiohttp async def run(): for _ in range(10): async with aiohttp.ClientSession() as session: response = await session.get( "http://localhost:4566/bucket/ae4bbfcdc47f450aa8557abefeba4a5ct", ) i = 0 async for chunk in response.content.iter_chunked(1024): i += 1 print(f"chunk {i}") # This process only fails near the end of the download, this condition is just here to speed up the testing # by skipping some uploads but the bug reproduces without it, it just takes more time. if i >= 900: print("Streamed, time to upload") # It hangs awaiting this, no timeout is raised await session.put( "http://localhost:4566/bucket/output/some_file", data=b"" ) print("Uploaded") asyncio.run(run())

Environment

  • Python: 3.12

What Broke

The upload process hangs intermittently without raising exceptions.

Why It Broke

The connection was returned to the pool in a paused state after a streaming download

Fix Options (Details)

Option A — Apply the official fix

Fixes a hang when reusing a connection that was previously used for a streaming download and returned to the pool in paused state.

When NOT to use: Do not use this fix if connection reuse is not a concern in your application.

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

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 this fix if connection reuse is not a concern in your application.

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

  • Add a stress test that runs high-concurrency workloads and fails on thread dumps / blocked locks.
  • Enable watchdog dumps in prod (faulthandler, thread dump endpoint) to capture deadlocks 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.