When a user opens a post that does not exist, or visits the admin area without permission, the view must stop immediately and return the correct error code — not render an error template by hand and return it with status 200. Flask ships with abort(): it raises an HTTP exception, stops the view right there, and Flask returns the right status code with an error page. Combined with errorhandler(), you get a clean, standard error page for the whole application.
The Problem
The most common approach is to return an error string with a status code by hand: return "Post not found", 404. It works, but every view repeats the same pattern, and it is easy to forget the status code — leave out , 404 and the error page returns 200, so Google indexes your “not found” page and clients or APIs cannot tell the request failed. Worse is rendering an error template and returning normally: the page looks right, but the status code is still 200.
# Weak approach: easy to forget the status code, every view repeats a pattern @app.get("/posts/<int:post_id>") def view_post(post_id): post = Post.query.get(post_id) if post is None: return "Post not found", 404 # forget ", 404" and it becomes 200 return render_template("post.html", post=post) The Solution
abort() takes an HTTP error code and stops the view immediately — no code after it runs. To add a reason, pass description and Flask puts it on the default error page.
from flask import Flask, abort, render_template app = Flask(__name__) @app.get("/posts/<int:post_id>") def view_post(post_id): post = Post.query.get(post_id) if post is None: abort(404, description=f"No post #{post_id}") if post.is_private and not current_user.is_admin: abort(403, description="This post is for administrators only") return render_template("post.html", post=post) Flask’s default error page is quite plain, so register errorhandler() once for the whole app to render your own nice template — the error object already carries code and description to display:
@app.errorhandler(404) def page_not_found(error): return render_template("404.html", message=error.description), 404 @app.errorhandler(403) def forbidden(error): return render_template("403.html", message=error.description), 403 One important note: abort() only makes sense inside a request context — in a view, in before_request, or in a helper called by a view. Outside a request — for example in a background job or the Flask shell — it still raises the HTTP exception, but there is no request for Flask to turn that exception into a response, so it is just an unhandled exception.
Putting It Into Practice
A quick rule for picking codes: resource not found means abort(404), no permission means abort(403), malformed input means abort(400). Keep description short and user-facing (“No post #12”) instead of leaking internal details. And register errorhandler() for 404 and 403 from the start of the project — once, and every abort() in the app gets a consistent error page.
Related Posts
Flask: Use the Application Factory (create_app) to Make the App Easy to Test and Extend from Day One
Flask: Organize Routes Neatly with Blueprint as the Project Grows
Flask: Manage Config per Environment (dev/staging/prod), No More Hardcoded SECRET_KEY
FAQ
How is abort() different from return (“…”, 404)?
abort() raises an exception, so the view stops right there and the code after it never runs. A return with a tuple only sends the response after the view finishes — forget the status code and the response becomes 200, and you have to repeat this pattern in every view.
Does the user see the description?
Yes, if your error page uses it. Flask’s default error page already shows the description; with a hand-written errorhandler(), read it from error.description and pass it to the template as in the example above.
Can I use abort() outside a view?
You should not. Outside a request context, abort() still raises the HTTP exception (for example NotFound), but there is no request for Flask to turn that exception into a response — in a background job or the Flask shell it is just an unhandled exception. Use abort() only in views, before_request, or helpers called by a view.
How do I handle errors for an API?
Register an errorhandler() that returns JSON instead of HTML, for example {"error": error.description} with error.code. The client gets the right error code and message instead of having to parse an HTML page.
💬 Have you tried this approach in your own project? If you ran into an error or know a more concise way to do it, leave a comment below — I read every one and will add the best ideas to the post.
Sources
Flask documentation, Application Errors: https://flask.palletsprojects.com/en/stable/errorhandling/
Flask documentation, API — abort(): https://flask.palletsprojects.com/en/stable/api/#flask.abort
Originally published at linhfish.dev.



