The Fix
Upgrade to version 0.17.0 or later.
Based on closed Kludex/starlette issue #1255 · PR/commit linked
Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.
@@ -23,17 +23,25 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
async def call_next(request: Request) -> Response:
+ app_exc: typing.Optional[Exception] = None
send_stream, recv_stream = anyio.create_memory_object_stream()
import httpx
import asyncio
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware import Middleware
async def error(request):
raise RuntimeError("oops")
class MyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
return await call_next(request)
app = Starlette(routes=[Route("/error", error)], middleware=[Middleware(MyMiddleware)])
async def main():
async with httpx.AsyncClient(app=app) as client:
_ = await client.get("http://testserver/error")
asyncio.run(main())
Re-run: python example.py
Option A — Upgrade to fixed release\nUpgrade to version 0.17.0 or later.\nWhen NOT to use: This fix should not be used if the application relies on the previous behavior of raising ExceptionGroup.\n\n
Why This Fix Works in Production
- Trigger: $ python example.py
- Mechanism: The test_custom_middleware fails due to an ExceptionGroup raised by anyio in error views under BaseHTTPMiddleware
- Why the fix works: Prevent anyio.ExceptionGroup in error views under a BaseHTTPMiddleware, resolving the issue with test_custom_middleware failing. (first fixed release: 0.17.0).
- 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.9 in real deployments (not just unit tests).
- The test_custom_middleware fails due to an ExceptionGroup raised by anyio in error views under BaseHTTPMiddleware
- Surfaces as: $ python example.py
Proof / Evidence
- GitHub issue: #1255
- Fix PR: https://github.com/kludex/starlette/pull/1262
- First fixed release: 0.17.0
- 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.29
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Indeed, have been seeing this persistently even since an upgrade to 0.17.1: There's no pattern I can discern after a few hours of trying to…”
“@bmwiedemann I am getting the same issue on an asyncio setup as soon as any custom middleware is combined with a view that raises an…”
“@florimondmanca this error is happening to me too, is there any solution you'd recommend if we don't want to downgrade Starlette?”
“@Kludex awesome, do you know when that will be?”
Failure Signature (Search String)
- $ python example.py
Error Message
Stack trace
Error Message
-------------
$ python example.py
Traceback (most recent call last):
File "/Users/florimond/Desktop/test.py", line 26, in <module>
asyncio.run(main())
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/Users/florimond/Desktop/test.py", line 23, in main
_ = await client.get("http://testserver/error")
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 1722, in get
return await self.request(
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 1481, in request
response = await self.send(
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 1568, in send
response = await self._send_handling_auth(
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 1604, in _send_handling_auth
response = await self._send_handling_redirects(
File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/site-packages/httpx/_client.py", line 1640, in _send_handling_redirects
response = await self._send_single_request(request, timeout)
File "/Users/florimond/.pyenv/
... (truncated) ...
Stack trace
Error Message
-------------
File \".../lib/python3.7/site-packages/anyio/streams/memory.py\", line 81, in receive
return self.receive_nowait()
File \".../lib/python3.7/site-packages/anyio/streams/memory.py\", line 76, in receive_nowait
raise WouldBlock
anyio.WouldBlock
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File \".../lib/python3.7/site-packages/starlette/middleware/base.py\", line 41, in call_next
message = await recv_stream.receive()
File \".../lib/python3.7/site-packages/anyio/streams/memory.py\", line 101, in receive
raise EndOfStream
anyio.EndOfStream
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File \"...", line 324, in dispatch
response = await call_next(request)
File \".../lib/python3.7/site-packages/starlette/middleware/base.py\", line 45, in call_next
raise RuntimeError(\"No response returned.\")
Minimal Reproduction
import httpx
import asyncio
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.middleware import Middleware
async def error(request):
raise RuntimeError("oops")
class MyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
return await call_next(request)
app = Starlette(routes=[Route("/error", error)], middleware=[Middleware(MyMiddleware)])
async def main():
async with httpx.AsyncClient(app=app) as client:
_ = await client.get("http://testserver/error")
asyncio.run(main())
Environment
- Python: 3.9
What Broke
The test_custom_middleware fails, causing CI/CD pipeline failures and impacting deployment.
Why It Broke
The test_custom_middleware fails due to an ExceptionGroup raised by anyio in error views under BaseHTTPMiddleware
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.17.0 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/1262
First fixed release: 0.17.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix should not be used if the application relies on the previous behavior of raising ExceptionGroup.
Verify Fix
Re-run: python example.py
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.
- 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.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.17.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.