The Fix
Upgrade to version 0.20.1 or later.
Based on closed Kludex/starlette issue #1303 · PR/commit linked
@@ -30,6 +30,7 @@ filterwarnings=
ignore: run_until_first_complete is deprecated and will be removed in a future version.:DeprecationWarning
ignore: starlette\.middleware\.wsgi is deprecated and will be removed in a future release\.*:DeprecationWarning
+ ignore: Async generator 'starlette\.requests\.Request\.stream' was garbage collected before it had been exhausted.*:ResourceWarning
[coverage:run]
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def handler(request):
return JSONResponse({k: v for k, v in (await request.form()).items()})
app = Starlette(
debug=True,
routes=[
Route("/", handler, methods=["POST"]),
],
)
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.20.1 or later.\nWhen NOT to use: This fix is not applicable if the application does not handle multipart/form-data requests.\n\n
Why This Fix Works in Production
- Trigger: % curl -F '=bar' localhost:8000
- Mechanism: The multipart/form-data parser raises a KeyError when a subpart lacks a 'name' parameter in its Content-Disposition header
- Why the fix works: Raise `MultiPartException` when missing "name" field on `Content-Disposition` header to prevent KeyError. (first fixed release: 0.20.1).
Why This Breaks in Prod
- Shows up under Python 3.9 in real deployments (not just unit tests).
- The multipart/form-data parser raises a KeyError when a subpart lacks a 'name' parameter in its Content-Disposition header
- Surfaces as: % curl -F '=bar' localhost:8000
Proof / Evidence
- GitHub issue: #1303
- Fix PR: https://github.com/kludex/starlette/pull/1643
- First fixed release: 0.20.1
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.95
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.26
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“### Checklist <!-- Please make sure you check all these items before submitting your bug report. --> - [x] The bug is reproducible against the latest release and/or master. - [x] There are no similar issues or pull requests to fix it yet. #”
Failure Signature (Search String)
- % curl -F '=bar' localhost:8000
Error Message
Stack trace
Error Message
-------------
% curl -F '=bar' localhost:8000
Traceback (most recent call last):
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
await route.handle(scope, receive, send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
await self.app(scope, receive, send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
response = await func(request)
File "/tmp/github/./multipart-buglette.py", line 7, in handler
return JSONResponse({k: v for k, v in (await request.form()).items()})
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/requests.py", line 247, in form
self._form = await multipart_parser.parse()
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/formparsers.py", line 212, in parse
field_name = _user_safe_decode(options[b"name"], charset)
KeyError: b'name'
Stack trace
Error Message
-------------
% curl -H 'Content-Type: multipart/form-data' -d 'bogus' localhost:8000
Traceback (most recent call last):
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
await route.handle(scope, receive, send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
await self.app(scope, receive, send)
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
response = await func(request)
File "/tmp/github/./multipart-buglette.py", line 7, in handler
return JSONResponse({k: v for k, v in (await request.form()).items()})
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/requests.py", line 247, in form
self._form = await multipart_parser.parse()
File "/tmp/github/venv/lib/python3.9/site-packages/starlette/formparsers.py", line 177, in parse
parser = multipart.MultipartParser(boundary, callbacks)
File "/tmp/github/venv/lib/python3.
... (truncated) ...
Minimal Reproduction
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
async def handler(request):
return JSONResponse({k: v for k, v in (await request.form()).items()})
app = Starlette(
debug=True,
routes=[
Route("/", handler, methods=["POST"]),
],
)
Environment
- Python: 3.9
What Broke
Requests with missing 'name' parameters in multipart/form-data lead to server errors and unhandled exceptions.
Why It Broke
The multipart/form-data parser raises a KeyError when a subpart lacks a 'name' parameter in its Content-Disposition header
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.20.1 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/kludex/starlette/pull/1643
First fixed release: 0.20.1
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not applicable if the application does not handle multipart/form-data requests.
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.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.20.1 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.