Global error handler for any exception

Question:

Is there a way to add a global catch-all error handler in which I can change the response to a generic JSON response?

I can’t use the got_request_exception signal, as it is not allowed to modify the response (http://flask.pocoo.org/docs/0.10/signals/).

In contrast all signal handlers are executed in undefined order and do not modify any data.

I would prefer to not wrap the app.handle_exception function as that feels like internal API. I guess I’m after something like:

 @app.errorhandler()
 def handle_global_error(e):
     return "Global error"

Note the errorhandler does not take any parameters, meaning it would catch all exceptions/status codes which does not have a specific error handler attached to them. I know I can use errorhandler(500) or errorhandler(Exception) to catch exceptions, but if I do abort(409) for example, it will still return a HTML response.

Asked By: joscarsson

||

Answers:

You can use @app.errorhandler(Exception):

Demo (the HTTPException check ensures that the status code is preserved):

from flask import Flask, abort, jsonify
from werkzeug.exceptions import HTTPException

app = Flask('test')

@app.errorhandler(Exception)
def handle_error(e):
    code = 500
    if isinstance(e, HTTPException):
        code = e.code
    return jsonify(error=str(e)), code

@app.route('/')
def index():
    abort(409)

app.run(port=1234)

Output:

$ http get http://127.0.0.1:1234/
HTTP/1.0 409 CONFLICT
Content-Length: 31
Content-Type: application/json
Date: Sun, 29 Mar 2015 17:06:54 GMT
Server: Werkzeug/0.10.1 Python/3.4.3

{
    "error": "409: Conflict"
}

$ http get http://127.0.0.1:1234/notfound
HTTP/1.0 404 NOT FOUND
Content-Length: 32
Content-Type: application/json
Date: Sun, 29 Mar 2015 17:06:58 GMT
Server: Werkzeug/0.10.1 Python/3.4.3

{
    "error": "404: Not Found"
}

If you also want to override the default HTML exceptions from Flask (so that they also return JSON), add the following before app.run:

from werkzeug.exceptions import default_exceptions
for ex in default_exceptions:
    app.register_error_handler(ex, handle_error)

For older Flask versions (<=0.10.1, i.e. any non-git/master version at the moment), add the following code to your application to register the HTTP errors explicitly:

from werkzeug import HTTP_STATUS_CODES
for code in HTTP_STATUS_CODES:
    app.register_error_handler(code, handle_error)
Answered By: ThiefMaster

Far from elegant, but the following works for tying all subclasses of HTTPException to a single error handler:

from flask import jsonify
from werkzeug.exceptions import HTTPException

def handle_error(error):
    code = 500
    if isinstance(error, HTTPException):
        code = error.code
    return jsonify(error='error', code=code)

for cls in HTTPException.__subclasses__():
    app.register_error_handler(cls, handle_error)
Answered By: bwind

This is Flask 0.12 compatible, and a very good solution to the problem (it allows one to render errors in JSON or any other format)

from functools import wraps
from flask import Flask, redirect, jsonify
app = Flask(__name__)

def get_http_exception_handler(app):
    """Overrides the default http exception handler to return JSON."""
    handle_http_exception = app.handle_http_exception
    @wraps(handle_http_exception)
    def ret_val(exception):
        exc = handle_http_exception(exception)    
        return jsonify({'code':exc.code, 'message':exc.description}), exc.code
    return ret_val

# Override the HTTP exception handler.
app.handle_http_exception = get_http_exception_handler(app)

https://github.com/pallets/flask/issues/671#issuecomment-12746738

Answered By: lol

If the Exceptions doesn’t work, you may try app.register_error_handler (or use app.errorhandler in a non-decorator way)

Source: https://github.com/pallets/flask/issues/1837

Answered By: Suresh Ganta

A cleaner way to implement this in Flask >=0.12 would be to explicitly register the handler for every Werkzeug exception:

from flask import jsonify
from werkzeug.exceptions import HTTPException, default_exceptions

app = Flask('test')

def handle_error(error):
    code = 500
    if isinstance(error, HTTPException):
        code = error.code
    return jsonify(error='error', code=code)

for exc in default_exceptions:
    app.register_error_handler(exc, handle_error)
Answered By: nfvs

Based on
Plain (non-HTML) error pages in REST api

I wanted to return json without changing any of my code at all, so I just added the following on the top of my code

@app.errorhandler(500)
def error_500(exception):
    return jsonify({"error": str(exception)}), 500, {'Content-Type': 'application/json'}

@app.errorhandler(400)
def error_400(exception):
    return jsonify({"error": str(exception)}), 400, {'Content-Type': 'application/json'}
Answered By: julianalimin

It is possible to register error handlers for very generic base classes such as HTTPException or even Exception. However, be aware that these will catch more than you might expect.

For example, an error handler for HTTPException might be useful for turning the default HTML errors pages into JSON. However, this handler will trigger for things you don’t cause directly, such as 404 and 405 errors during routing. Be sure to craft your handler carefully so you don’t lose information about the HTTP error.

from flask import Flask, abort, jsonify, json
from werkzeug.exceptions import HTTPException


app = Flask('test')
app.config['JSON_SORT_KEYS'] = False


@app.errorhandler(HTTPException)
def handle_exception(e):
    """Return JSON instead of HTML for HTTP errors."""
    # start with the correct headers and status code from the error
    response = e.get_response()
    # replace the body with JSON
    response.data = json.dumps({
        "error": {
            "code": e.code,
            "name": e.name,
            "description": e.description,
        }
    })
    print(response.data) 
    response.content_type = "application/json"
    return response


@app.route('/')
def index():
    abort(409)


@app.route('/aloha')
def aloha():
    abort(400, "I'm not in the mood to talk!")


app.run(port=1234)

output:

enter image description here

enter image description here

enter image description here

An error handler for Exception might seem useful for changing how all errors, even unhandled ones, are presented to the user. However, this is similar to doing except Exception: in Python, it will capture all otherwise unhandled errors, including all HTTP status codes.

In most cases it will be safer to register handlers for more specific exceptions. Since HTTPException instances are valid WSGI responses, you could also pass them through directly.

from werkzeug.exceptions import HTTPException

@app.errorhandler(Exception)
def handle_exception(e):
    # pass through HTTP errors
    if isinstance(e, HTTPException):
        return e

    # now you're handling non-HTTP exceptions only
    return render_template("500_generic.html", e=e), 500

Error handlers still respect the exception class hierarchy. If you register handlers for both HTTPException and Exception, the Exception handler will not handle HTTPException subclasses because it the HTTPException handler is more specific.

Answered By: Milovan Tomašević