The Fix
Fixes the 'No response returned' RuntimeError in BaseHTTPMiddleware by preventing polling for disconnects in StreamingResponse.
Based on closed Kludex/starlette issue #2516 · PR/commit linked
Production note: This tends to surface only under concurrency. Reproduce with load tests and watch for lock contention/cancellation paths.
@@ -6,9 +6,8 @@
from starlette._utils import collapse_excgroups
-from starlette.background import BackgroundTask
from starlette.requests import ClientDisconnect, Request
-from starlette.responses import ContentStream, Response, StreamingResponse
import asyncio
from hypercorn.asyncio import serve
from hypercorn.config import Config
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.middleware.base import RequestResponseEndpoint
from starlette.middleware import Middleware
class DummyMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
return await call_next(request)
async def homepage(request):
return JSONResponse({})
app = Starlette(
routes=[
Route("/", homepage),
],
middleware=[Middleware(DummyMiddleware) for i in range(100)], # Yes, 100x the same middleware.
)
config = Config.from_mapping({})
config.bind = ["127.0.0.1:8000"]
asyncio.run(serve(app, config))
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Apply the official fix\nFixes the 'No response returned' RuntimeError in BaseHTTPMiddleware by preventing polling for disconnects in StreamingResponse.\nWhen NOT to use: This fix should not be applied if the application relies on the previous behavior of BaseHTTPMiddleware.\n\n
Why This Fix Works in Production
- Trigger: `RuntimeError("No response returned")` in `BaseHTTPMiddleware`
- Mechanism: The error occurs due to a race condition in BaseHTTPMiddleware when handling client disconnects
- 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.11 in real deployments (not just unit tests).
- The error occurs due to a race condition in BaseHTTPMiddleware when handling client disconnects
- Surfaces as: `RuntimeError("No response returned")` in `BaseHTTPMiddleware`
Proof / Evidence
- GitHub issue: #2516
- Fix PR: https://github.com/encode/starlette/pull/2620
- Fix commit: https://github.com/kludex/starlette/commit/d771bb7a5ecfcd658735cf07ba6f1dd4ac057ccd
- Reproduced locally: No (not executed)
- Last verified: 2026-02-11
- Confidence: 0.60
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.50
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“We were seeing this as well on py3.12, dep versions: I'm downgrading us to: Which seems like it'll resolve the issue, but I'll report back…”
“This fix introduced a backward incompatible change (https://github.com/encode/starlette/pull/2620). Fixed that in https://github.com/encode/starlette/pull/2687. Ensured the original issue is resolved. @Kludex @adriangb please review. Thank you”
“FYI: We’ve reproduced the issue using starlette==0.47.2, sse-starlette==2.4.1 and fastapi==0.116.1; the client is a mobile app. Sending a message from the server at the start…”
“Hi, seems like we encountered a similar issue here”
Failure Signature (Search String)
- `RuntimeError("No response returned")` in `BaseHTTPMiddleware`
Error Message
Stack trace
Error Message
-------------
`RuntimeError("No response returned")` in `BaseHTTPMiddleware`
Minimal Reproduction
import asyncio
from hypercorn.asyncio import serve
from hypercorn.config import Config
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.middleware.base import RequestResponseEndpoint
from starlette.middleware import Middleware
class DummyMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
return await call_next(request)
async def homepage(request):
return JSONResponse({})
app = Starlette(
routes=[
Route("/", homepage),
],
middleware=[Middleware(DummyMiddleware) for i in range(100)], # Yes, 100x the same middleware.
)
config = Config.from_mapping({})
config.bind = ["127.0.0.1:8000"]
asyncio.run(serve(app, config))
Environment
- Python: 3.11
What Broke
Clients experience intermittent 'No response returned' errors during high load conditions.
Why It Broke
The error occurs due to a race condition in BaseHTTPMiddleware when handling client disconnects
Fix Options (Details)
Option A — Apply the official fix
Fixes the 'No response returned' RuntimeError in BaseHTTPMiddleware by preventing polling for disconnects in StreamingResponse.
Fix reference: https://github.com/encode/starlette/pull/2620
Last verified: 2026-02-11. Validate in your environment.
When NOT to Use This Fix
- This fix should not be applied if the application relies on the previous behavior of BaseHTTPMiddleware.
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 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.
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.