Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ exception_handlers = {
}
```

These handlers are passed to both `ServerErrorMiddleware` and `ExceptionMiddleware`, meaning they
handle unhandled exceptions (e.g., `RuntimeError`) as well as `HTTPException(status_code=500)`.

It's important to notice that in case a [`BackgroundTask`](background.md) raises an exception,
it will be handled by the `handle_error` function, but at that point, the response was already sent. In other words,
the response created by `handle_error` will be discarded. In case the error happens before the response was sent, then
Expand Down
3 changes: 1 addition & 2 deletions starlette/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ def build_middleware_stack(self) -> ASGIApp:
for key, value in self.exception_handlers.items():
if key in (500, Exception):
error_handler = value
else:
exception_handlers[key] = value
exception_handlers[key] = value

middleware = (
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
Expand Down
23 changes: 23 additions & 0 deletions tests/test_applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,29 @@ def test_500(test_client_factory: TestClientFactory) -> None:
assert response.json() == {"detail": "Server Error"}


def test_500_status_handler(test_client_factory: TestClientFactory) -> None:
"""
Test that a custom 500 status handler is invoked by ExceptionMiddleware
when an HTTPException with status_code=500 is raised.
"""

async def custom_500_handler(request: Request, exc: Exception) -> JSONResponse:
return JSONResponse({"detail": "Custom 500"}, status_code=500)

async def raise_http_500(request: Request) -> None:
raise HTTPException(status_code=500, detail="Server Error")

app = Starlette(
routes=[Route("/http-500", endpoint=raise_http_500)],
exception_handlers={500: custom_500_handler},
)

client = test_client_factory(app, raise_server_exceptions=False)
response = client.get("/http-500")
assert response.status_code == 500
assert response.json() == {"detail": "Custom 500"}


def test_websocket_raise_websocket_exception(client: TestClient) -> None:
with client.websocket_connect("/ws-raise-websocket") as session:
response = session.receive()
Expand Down