63

Using a python flask server, I want to be able to throw an http error response with the abort command and use a custom response string and a custom message in the body

@app.errorhandler(400)
def custom400(error):
    response = jsonify({'message': error.message})
    response.status_code = 404
    response.status = 'error.Bad Request'
    return response

abort(400,'{"message":"custom error message to appear in body"}')

But the error.message variable comes up as an empty string. I can't seem to find documentation on how to get access to the second variable of the abort function with a custom error handler

richmb
  • 1,495
  • 2
  • 15
  • 20

4 Answers4

98

If you look at flask/__init__.py you will see that abort is actually imported from werkzeug.exceptions. Looking at the Aborter class, we can see that when called with a numeric code, the particular HTTPException subclass is looked up and called with all of the arguments provided to the Aborter instance. Looking at HTTPException, paying particular attention to lines 85-89 we can see that the second argument passed to HTTPException.__init__ is stored in the description property, as @dirn pointed out.

You can either access the message from the description property:

@app.errorhandler(400)
def custom400(error):
    response = jsonify({'message': error.description['message']})
    # etc.

abort(400, {'message': 'custom error message to appear in body'})

or just pass the description in by itself:

@app.errorhandler(400)
def custom400(error):
    response = jsonify({'message': error.description})
    # etc.

abort(400, 'custom error message to appear in body')
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • 2
    This is a great way to extend abort's functionality with custom error messages. Thank you for this – pbojinov Aug 10 '14 at 00:30
  • 2
    Is there a way to do this with every status code instead of having to write a custom error handler for each code? Ex: `abort(409, 'There was a time conflict')`, `abort(400, 'custom error message to appear in body')` – Alex Cory Mar 10 '17 at 00:35
  • Duplicate answer - https://stackoverflow.com/a/45412576/853167 – debovis Dec 29 '20 at 15:22
74

People rely on abort() too much. The truth is that there are much better ways to handle errors.

For example, you can write this helper function:

def bad_request(message):
    response = jsonify({'message': message})
    response.status_code = 400
    return response

Then from your view function you can return an error with:

@app.route('/')
def index():
    if error_condition:
        return bad_request('message that appears in body')

If the error occurs deeper in your call stack in a place where returning a response isn't possible then you can use a custom exception. For example:

class BadRequestError(ValueError):
    pass

@app.errorhandler(BadRequestError)
def bad_request_handler(error):
    return bad_request(str(error))

Then in the function that needs to issue the error you just raise the exception:

def some_function():
    if error_condition:
        raise BadRequestError('message that appears in the body')

I hope this helps.

Erik Eng
  • 121
  • 7
Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152
  • 1
    is it preferable (faster) to `return bad_request(...)` directly when you can rather than just `raise BadRequestError(...)` ? – Ciprian Tomoiagă May 27 '19 at 19:40
  • You can raise abort anywhere when you want, no need to return. – Minh Hoàng Jul 07 '20 at 04:23
  • How is this any better? When not done from a view (and you need to register the error handler), the only difference is that you call `raise BadRequestError` instead of `abort`, with the added complication of having to register a custom exception. – nirvana-msu Jan 05 '21 at 01:47
41

I simply do it like this:

    abort(400, description="Required parameter is missing")
Vasili Pascal
  • 3,102
  • 1
  • 27
  • 21
  • The best answer in my opinion. – Z.Wei Jul 02 '19 at 14:24
  • When you want to return a json type response. You need make_response. – Minh Hoàng Jul 07 '20 at 04:25
  • 1
    To clarify Minh's comment from Jul 7, in `Werkzeug>=1.0.1` the code in this answer will generate a proper JSON response. I was even able to pass a `dict` as description and Werkzeug correctly generated a nested JSON response. – Peter Ilfrich Dec 02 '20 at 02:24
  • 1
    Unfortunately this returns HTML I do not want. I just want the message to print ONLY. Here is what curl output looks like: ` 403 Forbidden

    Forbidden

    Required parameter is missing

    `
    – Dave Jun 03 '21 at 00:25
10

flask.abort also accepts flask.Response

abort(make_response(jsonify(message="Error message"), 400))
MarredCheese
  • 17,541
  • 8
  • 92
  • 91
Neil
  • 300
  • 5
  • 10