FastAPI / uvicorn Exception in ASGI application (tested)
ERROR: Exception in ASGI application is uvicorn reporting that an exception escaped your FastAPI app; the client just gets a plain-text 500. The cause is at the bottom of the traceback: usually a bug in the endpoint, a ResponseValidationError, or a return value that can't be encoded as JSON.
1. Reproduce it: three ways to get the 500
The app (app.py), run with python -m uvicorn app:app:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Out(BaseModel):
id: int
name: str
@app.get("/boom")
def boom():
return {"ratio": 1 / 0}
@app.get("/http")
def http():
raise HTTPException(status_code=404, detail="no such item")
@app.get("/model", response_model=Out)
def model():
return {"id": 1} # 'name' missing from the response
@app.get("/obj")
def obj():
return object()
A client hitting each route:
import urllib.request, urllib.error
for path in ("/boom", "/http", "/model", "/obj"):
try:
r = urllib.request.urlopen("http://127.0.0.1:8000" + path)
print(path, r.status, r.read().decode())
except urllib.error.HTTPError as e:
print(path, e.code, e.headers["content-type"], e.read().decode())
Output:
/boom 500 text/plain; charset=utf-8 Internal Server Error
/http 404 application/json {"detail":"no such item"}
/model 500 text/plain; charset=utf-8 Internal Server Error
/obj 500 text/plain; charset=utf-8 Internal Server Error
The client only ever sees a plain-text Internal Server Error. The real error is in the server log. Here it is with the ~60 lines of uvicorn/Starlette/FastAPI frames in each traceback cut out (everything else is verbatim):
INFO: ... "GET /boom HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
... (about 60 framework lines) ...
File "/w/app.py", line 9, in boom
return {"ratio": 1 / 0}
~~^~~
ZeroDivisionError: division by zero
INFO: ... "GET /http HTTP/1.1" 404 Not Found
INFO: ... "GET /model HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
... (about 60 framework lines) ...
fastapi.exceptions.ResponseValidationError: 1 validation error:
{'type': 'missing', 'loc': ('response', 'name'), 'msg': 'Field required', 'input': {'id': 1}}
File "/w/app.py", line 13, in model
INFO: ... "GET /obj HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
... (about 60 framework lines) ...
return jsonable_encoder(response_content)
File "/usr/local/lib/python3.14/site-packages/fastapi/encoders.py", line 355, in jsonable_encoder
raise ValueError(errors) from e
ValueError: [TypeError("'object' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
"Exception in ASGI application" is just uvicorn saying an exception escaped your app. Ignore the framework frames and read the last line (the exception) and the last File line that points into your own code. The three cases above are the common ones:
- A bug in the endpoint (here
ZeroDivisionError): fix the code; the frame just above the last line is your file and line number. ResponseValidationError: what you returned does not matchresponse_model(or the return annotation). Thelocstarts withresponse, e.g.('response', 'name'); return the missing field or fix the model.ValueErrorfromjsonable_encoder: you returned an object FastAPI cannot turn into JSON. Return a dict, a Pydantic model, or aResponse.
HTTPException is not an error in this sense: /http returned a clean 404 JSON response and logged only the access line, no traceback.
2. Return JSON instead of plain text, and log it your way
import logging
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
log = logging.getLogger("app")
app = FastAPI()
@app.exception_handler(Exception)
async def unhandled(request: Request, exc: Exception):
log.exception("unhandled error on %s", request.url.path)
return JSONResponse({"detail": "internal error"}, status_code=500)
@app.get("/boom")
def boom():
return {"ratio": 1 / 0}
c = TestClient(app, raise_server_exceptions=False)
r = c.get("/boom")
print(r.status_code, r.headers["content-type"], r.text)
try:
TestClient(app).get("/boom")
except ZeroDivisionError as e:
print("default TestClient re-raises:", repr(e))
Output:
500 application/json {"detail":"internal error"}
default TestClient re-raises: ZeroDivisionError('division by zero')
An @app.exception_handler(Exception) turns the plain-text 500 into a JSON one and gives you a place to log with your own logger (log.exception records the traceback). Note the second line: Starlette's TestClient re-raises server exceptions by default, so in tests you see the real exception directly; pass raise_server_exceptions=False to test the 500 response itself.
Checklist
- Read the last line of the traceback and the last frame in your own file; skip the uvicorn/starlette frames.
ResponseValidationErrorwithlocstartingresponse: your return value does not matchresponse_model.ValueErrorinjsonable_encoder: you returned a non-serialisable object.- Want expected errors to be 4xx, not 500? Raise
HTTPException. - App won't even start? That is a different error: see uvicorn "Error loading ASGI app".
Related: FastAPI 422 Unprocessable Entity, Pydantic "Field required".
Frequently asked questions
What does Exception in ASGI application mean?
uvicorn caught an unhandled exception from your app and returned a 500. The real error is the last line of the traceback in the server log.
What is ResponseValidationError in FastAPI?
Your endpoint's return value does not match its response_model; the loc, e.g. ('response', 'name'), shows the missing or wrong field.
How do I return JSON for FastAPI 500 errors?
Register @app.exception_handler(Exception) that logs the exception and returns JSONResponse(..., status_code=500).
Written with AI assistance. Measured on 2026-10-23 with FastAPI 0.161.1, uvicorn 0.54.0 and Python 3.14.7 (python:3-alpine in Docker). Blocks were run verbatim; the server log is unedited except for the framework frames marked as cut. PlainHeaders is our extension; Python, FastAPI and uvicorn are free.