The Fix
Upgrade to version 0.46.0 or later.
Based on closed Kludex/starlette issue #2625 · PR/commit linked
Production note: Most teams hit this during upgrades or environment changes. Roll out with a canary and smoke critical endpoints (health, OpenAPI/docs) before 100%.
@@ -103,10 +103,9 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
wrapped_receive = request.wrapped_receive
response_sent = anyio.Event()
+ app_exc: Exception | None = None
async def call_next(request: Request) -> Response:
import uvicorn
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
class MyExc(Exception): # change to BaseException and then both exceptions are sent to stdout
...
@app.get("/info")
def info():
# raises Exception as expected, the traceback is seen in console
raise MyExc
private_api = FastAPI()
@private_api.get("/info")
def info():
# exception is handled silently, no traceback is seen in console
raise MyExc
app.mount("/private", private_api)
class Middleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
return await call_next(request)
app.add_middleware(Middleware) # when this is removed, the exceptions are raised for all routes
if __name__ == "__main__":
uvicorn.run(app, port=8000)
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.46.0 or later.\nWhen NOT to use: This fix should not be applied if the middleware behavior is intentionally designed to suppress exceptions.\n\nOption C — Workaround\nfunctions for me when the exception occurs in my endpoint's code. But if the endpoint starts a background task and that task throws an exception, it gets lost.\nWhen NOT to use: This fix should not be applied if the middleware behavior is intentionally designed to suppress exceptions.\n\n
Why This Fix Works in Production
- Trigger: middleware causes exceptions to not be raised/handled silently (back again)
- Mechanism: The middleware was swallowing exceptions instead of propagating them
- Why the fix works: Raises exceptions from background tasks in BaseHTTPMiddleware, addressing the issue of swallowed exceptions. (first fixed release: 0.46.0).
- If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.
Why This Breaks in Prod
- The middleware was swallowing exceptions instead of propagating them
- Production symptom (often without a traceback): middleware causes exceptions to not be raised/handled silently (back again)
Proof / Evidence
- GitHub issue: #2625
- Fix PR: https://github.com/encode/starlette/pull/2812
- First fixed release: 0.46.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.51
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“@daniilmastrangeli a fix I have found for now is to add_exception_handler which catches the exception and if re-raised will display the trace to stdout as…”
“The root cause is here: https://github.com/encode/starlette/pull/2194#issuecomment-2235897188. For more references, see also: https://github.com/fastapi/fastapi/discussions/8577#discussioncomment-8914357”
“I've started some work on https://github.com/encode/starlette/pull/2696.”
“Is there a way to work around this for background tasks as well? The workaround functions for me when the exception occurs in my endpoint's…”
Failure Signature (Search String)
- middleware causes exceptions to not be raised/handled silently (back again)
- Regression of #1976 #1977 #1609 #1940
Copy-friendly signature
Failure Signature
-----------------
middleware causes exceptions to not be raised/handled silently (back again)
Regression of #1976 #1977 #1609 #1940
Error Message
Signature-only (no traceback captured)
Error Message
-------------
middleware causes exceptions to not be raised/handled silently (back again)
Regression of #1976 #1977 #1609 #1940
Minimal Reproduction
import uvicorn
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
class MyExc(Exception): # change to BaseException and then both exceptions are sent to stdout
...
@app.get("/info")
def info():
# raises Exception as expected, the traceback is seen in console
raise MyExc
private_api = FastAPI()
@private_api.get("/info")
def info():
# exception is handled silently, no traceback is seen in console
raise MyExc
app.mount("/private", private_api)
class Middleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
return await call_next(request)
app.add_middleware(Middleware) # when this is removed, the exceptions are raised for all routes
if __name__ == "__main__":
uvicorn.run(app, port=8000)
What Broke
Exceptions raised in endpoints were not logged, leading to silent failures.
Why It Broke
The middleware was swallowing exceptions instead of propagating them
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.46.0 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Option C — Workaround Temporary workaround
functions for me when the exception occurs in my endpoint's code. But if the endpoint starts a background task and that task throws an exception, it gets lost.
Use only if you cannot change versions today. Treat this as a stopgap and remove once upgraded.
Fix reference: https://github.com/encode/starlette/pull/2812
First fixed release: 0.46.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix should not be applied if the middleware behavior is intentionally designed to suppress exceptions.
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
- Capture the exact failing error string in logs and tests so you can reproduce via a minimal script.
- Pin production dependencies and upgrade only with a reproducible test that hits the failing path.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.46.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.