question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Custom 500 or Exception handlers do not run through middleware like other handled exceptions.

See original GitHub issue

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

To reproduce

from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse

app = Starlette(routes=routes, middleware=middleware)

async def not_found(request, exc):
    return JSONResponse(content={"error": 404}, status_code=exc.status_code)

async def server_error(request, exc):
    return JSONResponse(content={"error": 500}, status_code=exc.status_code)

exception_handlers = {
    404: not_found,
    500: server_error
}

middleware = [
    Middleware(CORSMiddleware, allow_origins=['*'])
]

app = Starlette(routes=routes, middleware=middleware, exception_handlers=exception_handlers)

Expected behavior

both 404 and 500 errors will respond with their relevant json response, and correct CORS headers

Actual behavior

the 404 error will response with correct CORS headers but the 500 error will not.

Debugging material

Because Exception and 500 are special cased in build_middleware_stack any error bubbles up to the outer most ServerErrorMiddleware which is outside the other middleware, instead of being handled by the ExceptionMiddleware and passing through the custom middleware.

see: https://github.com/encode/starlette/blob/master/starlette/applications.py#L74-L77

I can see four possible fixes, none of them perfect…

  • handle custom 500 errors in ExceptionMiddleware and prevent ServerErrorMiddleware showing its helpful debug info when a custom handler is present.

  • have ServerErrorMiddleware run the response of the error handler through the middleware stack again, and if that fails resort to existing behaviour, adding a bunch of complexity.

  • move the debuging info to a separate middleware and put that in front of the ErrorMiddleware, which would then cause it to trigger for every exception, but would allow ExceptionMiddleware handle 500s as expected, and ServerErrorMiddleware becomes a last resort.

  • update https://www.starlette.io/exceptions/ to explain 500 errors are handled differently, and to get the above behaviour something like the following work-around is required:

    @app.middleware("http")
    def exception_middleware(request, call_next)
        try:
            return await call_next(request)
        except Exception:
            return JSONResponse(content={"error": 500}, status_code=500))
    

Environment

  • OS: N/A
  • Python version: N/A
  • Starlette version: 0.13.6 and master branch

Additional Context

#1116 seems to be have similar but refers to the generic error_response on ServerErrorMiddleware, this is similar but specifically about custom error handlers so i didn’t want to hijack that issue.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:7
  • Comments:17 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
JimDabellcommented, Feb 11, 2022

We serialise errors to Problem JSON format and display them in the browser, including 500 errors. Just because an error has occurred on the server, it doesn’t mean that you can’t show a useful error to the user containing the details of what has gone wrong. Right now, the obvious approach you get from following the documentation fails. Instead you need a clumsy workaround that goes against the documentation’s recommendation.

It’s a broken state that you do want to see

That’s precisely the point. If you have an SPA loading API requests and there is a 500 error, the CORS headers are what allows you to see what is going on with the broken state, instead of just "Sorry, something failed but I can’t tell you what because I’m not allowed to look at the details”.

2reactions
tomchristiecommented, Feb 11, 2022

the 404 error will response with correct CORS headers but the 500 error will not.

That… seems totally okay to me.

If you have an unhandled exception you really do just want to break out with a 500 error, with minimal other interference.

It’s a broken state that you do want to see, and it shouldn’t at all matter what CORS headers it does or doesn’t include.

(Also it’d be perfectly possible for the user to get a 5xx response from some intermediary, such as an overloaded server, or a gateway proxy, and you wouldn’t be seeing any CORS headers on those either)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Handle errors in ASP.NET Core | Microsoft Learn
The status code pages middleware does not catch exceptions. To provide a custom error handling page, use the exception handler page.
Read more >
Creating a custom ErrorHandlerMiddleware function
In this post I show how to customise the ExceptionHandlerMiddleware to create custom responses when an error occurs in your middleware ...
Read more >
ASP.Net Core exception handling middleware - Stack Overflow
It seems that app.UseDeveloperExceptionPage();. was preventing the exception handling middleware from catching exceptions.
Read more >
Error handling - Express.js
It's important to ensure that Express catches all errors that occur while running route handlers and middleware. Errors that occur in synchronous code...
Read more >
ASP.NET Core global exception handling gotchas
NET global exception handler is usually done for purposes such as: ... which is not something that we get when using the custom...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found