The Fix
Upgrade to version 0.11.4 or later.
Based on closed Kludex/uvicorn issue #188 · PR/commit linked
@@ -26,8 +26,9 @@ def isclosed(self):
class _ASGIAdapter(requests.adapters.HTTPAdapter):
- def __init__(self, app: typing.Callable) -> None:
+ def __init__(self, app: typing.Callable, raise_server_exceptions=True) -> None:
self.app = app
import asyncio
import logging
# import warnings
LOGGER = logging.getLogger()
async def never_called():
LOGGER.debug("I'm never actually called.")
async def uncomsumed():
LOGGER.debug("My exception is never consumed.")
raise ValueError("Ouch")
async def transport():
LOGGER.debug("Rapid")
connect = asyncio.open_connection("http://google.com", 80)
reader, writer = await connect
async def create():
await asyncio.sleep(3.0)
LOGGER.debug("(1) create file")
async def write():
await asyncio.sleep(1.0)
LOGGER.debug("(2) write into file")
async def close():
LOGGER.debug("(3) close file")
async def pending():
asyncio.ensure_future(create())
asyncio.ensure_future(write())
asyncio.ensure_future(close())
await asyncio.sleep(2.0)
loop.stop()
if __name__ == "__main__":
# warnings.simplefilter("always", ResourceWarning)
logging.basicConfig(level="DEBUG",
format="[%(name)s] [%(levelname)s] %(message)s")
loop = asyncio.get_event_loop()
# loop.set_debug(True)
never_called()
asyncio.ensure_future(uncomsumed())
asyncio.ensure_future(transport())
asyncio.ensure_future(pending())
loop.run_forever()
LOGGER.warning("Pending tasks at exit: %r.", asyncio.Task.all_tasks(loop))
loop.close()
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.11.4 or later.\nWhen NOT to use: This fix is not suitable if you need to suppress exceptions intentionally.\n\n
Why This Fix Works in Production
- Trigger: I'm able to get tracebacks about uncalled co-routines via environment variable `PYTHONASYNCIODEBUG=1` or inside the application with:
- Mechanism: DebugMiddleware was swallowing exceptions when returning 500 responses
- Why the fix works: Resolves an issue where DebugMiddleware was swallowing exceptions when returning 500 responses, allowing them to be logged properly. (first fixed release: 0.11.4).
Why This Breaks in Prod
- Shows up under Python 3.6 in real deployments (not just unit tests).
- DebugMiddleware was swallowing exceptions when returning 500 responses
- Production symptom (often without a traceback): I'm able to get tracebacks about uncalled co-routines via environment variable `PYTHONASYNCIODEBUG=1` or inside the application with:
Proof / Evidence
- GitHub issue: #188
- Fix PR: https://github.com/encode/uvicorn/pull/190
- First fixed release: 0.11.4
- 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.39
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“Sorry, I’m slightly lost”
“> I guess my only remaining problem is that I'm raising an exception (ValueError) in one of my routes and I had expected to see…”
“> Sorry, I’m slightly lost”
“Probably not especially related but worth noting - uvicorn uses the root logger in 0.3.2, but uses a properly named logger in 0.3.3.”
Failure Signature (Search String)
- I'm able to get tracebacks about uncalled co-routines via environment variable `PYTHONASYNCIODEBUG=1` or inside the application with:
- Adding the handler on the `uvicorn` logger and separately changing the level should not be necessary due to the changes on the `root` logger but I was kinda desperate to try
Copy-friendly signature
Failure Signature
-----------------
I'm able to get tracebacks about uncalled co-routines via environment variable `PYTHONASYNCIODEBUG=1` or inside the application with:
Adding the handler on the `uvicorn` logger and separately changing the level should not be necessary due to the changes on the `root` logger but I was kinda desperate to try something.
Error Message
Signature-only (no traceback captured)
Error Message
-------------
I'm able to get tracebacks about uncalled co-routines via environment variable `PYTHONASYNCIODEBUG=1` or inside the application with:
Adding the handler on the `uvicorn` logger and separately changing the level should not be necessary due to the changes on the `root` logger but I was kinda desperate to try something.
Minimal Reproduction
import asyncio
import logging
# import warnings
LOGGER = logging.getLogger()
async def never_called():
LOGGER.debug("I'm never actually called.")
async def uncomsumed():
LOGGER.debug("My exception is never consumed.")
raise ValueError("Ouch")
async def transport():
LOGGER.debug("Rapid")
connect = asyncio.open_connection("http://google.com", 80)
reader, writer = await connect
async def create():
await asyncio.sleep(3.0)
LOGGER.debug("(1) create file")
async def write():
await asyncio.sleep(1.0)
LOGGER.debug("(2) write into file")
async def close():
LOGGER.debug("(3) close file")
async def pending():
asyncio.ensure_future(create())
asyncio.ensure_future(write())
asyncio.ensure_future(close())
await asyncio.sleep(2.0)
loop.stop()
if __name__ == "__main__":
# warnings.simplefilter("always", ResourceWarning)
logging.basicConfig(level="DEBUG",
format="[%(name)s] [%(levelname)s] %(message)s")
loop = asyncio.get_event_loop()
# loop.set_debug(True)
never_called()
asyncio.ensure_future(uncomsumed())
asyncio.ensure_future(transport())
asyncio.ensure_future(pending())
loop.run_forever()
LOGGER.warning("Pending tasks at exit: %r.", asyncio.Task.all_tasks(loop))
loop.close()
Environment
- Python: 3.6
What Broke
Users were not seeing exceptions in logs during error responses.
Why It Broke
DebugMiddleware was swallowing exceptions when returning 500 responses
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.11.4 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/encode/uvicorn/pull/190
First fixed release: 0.11.4
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not suitable if you need to suppress exceptions intentionally.
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.11.4 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.