Connection reset after exception raised & handled in FastAPI #2285
-
Hi Uvicorn team, When using an exception handler in a FastAPI application, I am seeing subsequent requests terminated with the following issue:
I've included a sample application, script, and tcpdump that can be used to reproduce the behavior. I'd appreciate some help understanding if this is something misconfigured on my side, or if this is a bug. Thanks. How to reproduceSample App from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR, HTTP_400_BAD_REQUEST
from pydantic import BaseModel, Field
from uvicorn import run
class Message(BaseModel):
message: str
app = FastAPI(title="Test app")
@app.post("/error")
def error(body: Message) -> JSONResponse:
raise Exception(body.message)
@app.post("/ok")
def ok(body: Message) -> JSONResponse:
return JSONResponse(status_code=200, content="ok")
@app.exception_handler(Exception)
async def catch_all_exception_handler(request: Request, exc: Exception) -> JSONResponse:
return JSONResponse(
status_code=HTTP_400_BAD_REQUEST, content={"detail": str(exc)}
)
if __name__ == "__main__":
run(app, host="127.0.0.1", port=8000) Sample client script from requests.sessions import Session
def main():
session = Session()
resp = session.post("http://127.0.0.1:8000/error", json={"message": "test error"})
assert resp.status_code == 400
resp = session.post("http://127.0.0.1:8000/ok", json={"message": "test error"})
if __name__ == "__main__":
main() Logs from the client script
tcpdump of the server port while the client script is running
Interesting Observations
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 17 replies
-
What are the logs from the server side? |
Beta Was this translation helpful? Give feedback.
After taking another look today, I actually think I figured this out. It turns out, Starlette treats exception handlers that handle
Exception
differently than handlers that handle more specific exceptions:https://github.com/encode/starlette/blob/89fae174a1ea10f59ae248fe030d9b7e83d0b8a0/starlette/applications.py#L92-L92
For exception handlers that handle
Exception
, it uses aServerErrorMiddleware
class, which at the end of handling the exception, re-raises it to the Uvicorn layer:https://github.com/encode/starlette/blob/89fae174a1ea10f59ae248fe030d9b7e83d0b8a0/starlette/middleware/errors.py#L186-L186
Then, Uvicorn closes the client connection:
uvicorn/uvicorn/protocols/http/h11_impl.py