The Fix
Upgrade to version 0.18.3 or later.
Based on closed Kludex/uvicorn issue #1564 · PR/commit linked
@@ -145,6 +145,7 @@ def connection_lost(self, exc: Optional[Exception]) -> None:
if exc is None:
self.transport.close()
+ self._unset_keepalive_if_required()
def eof_received(self) -> None:
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.requests import Request
async def homepage(request: Request):
request.state.test = [x for x in range(999999)]
return JSONResponse({'hello': 'world'})
app = Starlette(routes=[
Route('/', homepage),
])
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\nUpgrade to version 0.18.3 or later.\nWhen NOT to use: This fix should not be applied if the application relies on the protocol's reference for other functionalities.\n\nOption C — Workaround\nthat I can use to resolve this by setting the the property to none at the end of the request - `request.state.test = None`.\nWhen NOT to use: This fix should not be applied if the application relies on the protocol's reference for other functionalities.\n\n
Why This Fix Works in Production
- Trigger: Memory leak when using request.state
- Mechanism: The HTTP protocol object contains a reference cycle that prevents it from being freed by the garbage collector
- Why the fix works: Removes the reference to the parser after losing connection to break the cyclic reference to the protocol, allowing the garbage collector to free up the object. (first fixed release: 0.18.3).
- If left unfixed, this can cause silent data inconsistencies that propagate (bad cache entries, incorrect downstream decisions).
Why This Breaks in Prod
- The HTTP protocol object contains a reference cycle that prevents it from being freed by the garbage collector
- Production symptom (often without a traceback): Memory leak when using request.state
Proof / Evidence
- GitHub issue: #1564
- Fix PR: https://github.com/kludex/uvicorn/pull/1604
- First fixed release: 0.18.3
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.85
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.66
Verified Execution
We executed the runnable minimal repro in a temporary environment and captured exit codes + logs.
- Status: PASS
- Ran: 2026-02-11T16:52:29Z
- Package: uvicorn
- Fixed: 0.18.3
- Mode: fixed_only
- Outcome: ok
Logs
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“The bug is confirmed to be on uvicorn. The behavior doesn't happen with hypercorn.”
“Alright, I gave it another try and I compared with h11 protocol that was well managing its memory, so in the end it seems that…”
“I'll be a bit away for the next two weeks, so if you solve it, feel free to create a PR. 🙏 If not, when…”
“Investigating... I can see this: https://github.com/encode/starlette/blob/e7717af33c8ae0cd8458886f7359c9753746ed5f/starlette/datastructures.py#L704-L705”
Failure Signature (Search String)
- Memory leak when using request.state
- I noticed that setting properties on request.state results in memory leak.
Copy-friendly signature
Failure Signature
-----------------
Memory leak when using request.state
I noticed that setting properties on request.state results in memory leak.
Error Message
Signature-only (no traceback captured)
Error Message
-------------
Memory leak when using request.state
I noticed that setting properties on request.state results in memory leak.
Minimal Reproduction
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.requests import Request
async def homepage(request: Request):
request.state.test = [x for x in range(999999)]
return JSONResponse({'hello': 'world'})
app = Starlette(routes=[
Route('/', homepage),
])
What Broke
Memory usage increases with each request, leading to potential outages due to resource exhaustion.
Why It Broke
The HTTP protocol object contains a reference cycle that prevents it from being freed by the garbage collector
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.18.3 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Option C — Workaround Temporary workaround
that I can use to resolve this by setting the the property to none at the end of the request - `request.state.test = None`.
Use only if you cannot change versions today. Treat this as a stopgap and remove once upgraded.
Fix reference: https://github.com/kludex/uvicorn/pull/1604
First fixed release: 0.18.3
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix should not be applied if the application relies on the protocol's reference for other functionalities.
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
- 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.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.18.3 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.