Jump to solution
Verify

The Fix

Breaks cyclic references at connection close when there was a traceback, addressing a potential memory leak.

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

Jump to Verify Open PR/Commit
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@ +Break cyclic references at connection close when there was a traceback -- by :user:`bdraco`. + +Special thanks to :user:`availov` for reporting the issue.
repro.py
async def main(): app = web.Application() app.router.add_get('/stream', stream_handler) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, 'localhost', 8080) await site.start() client = Client() client_task = asyncio.create_task(client.fetch_stream('http://localhost:8080/stream')) await client_task del client del client_task await asyncio.sleep(0.5) # Allow time for cleanup print(f'Garbage objects: {get_garbage()}') await runner.cleanup()
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\nBreaks cyclic references at connection close when there was a traceback, addressing a potential memory leak.\nWhen NOT to use: This fix is not applicable if cyclic references are not the root cause of memory issues.\n\n

Why This Fix Works in Production

  • Trigger: Memory Leak Due to Cyclic References in Traceback
  • Mechanism: Cyclic references in traceback objects prevent garbage collection of ClientResponse
Production impact:
  • If left unfixed, this can cause silent data inconsistencies that propagate (bad cache entries, incorrect downstream decisions).

Why This Breaks in Prod

  • Cyclic references in traceback objects prevent garbage collection of ClientResponse
  • Surfaces as: Memory Leak Due to Cyclic References in Traceback

Proof / Evidence

Discussion

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

“It certainly wouldn't be the first time I've seen problems with exception traces holding on to references”
@bdraco · 2025-03-10 · source
“It looks like the websocket has the same issue Moving the cleanup here also fixes the problem and likely covers all the cases”
@bdraco · 2025-03-15 · source
“Even if I add del client and del client_task I can still reproduce the leak so maybe a true leak”
@bdraco · 2025-03-15 · source

Failure Signature (Search String)

  • Memory Leak Due to Cyclic References in Traceback

Error Message

Stack trace
error.txt
Error Message ------------- Memory Leak Due to Cyclic References in Traceback

Minimal Reproduction

repro.py
async def main(): app = web.Application() app.router.add_get('/stream', stream_handler) runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, 'localhost', 8080) await site.start() client = Client() client_task = asyncio.create_task(client.fetch_stream('http://localhost:8080/stream')) await client_task del client del client_task await asyncio.sleep(0.5) # Allow time for cleanup print(f'Garbage objects: {get_garbage()}') await runner.cleanup()

What Broke

Memory leak observed with lingering ClientResponse objects after disconnection.

Why It Broke

Cyclic references in traceback objects prevent garbage collection of ClientResponse

Fix Options (Details)

Option A — Apply the official fix

Breaks cyclic references at connection close when there was a traceback, addressing a potential memory leak.

When NOT to use: This fix is not applicable if cyclic references are not the root cause of memory issues.

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

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

  • This fix is not applicable if cyclic references are not the root cause of memory issues.

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.

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.