10. Errors and local debugging
Things break. The difference between a frustrating afternoon and a quick fix is a short loop from symptom to cause. This page covers the two pieces that loop needs: error pages that keep a broken request inside your app instead of dumping a default white screen, and the tools that tell you exactly what went wrong and where.
Custom 404 and 500 pages
By default Flask shows a plain "Not Found" for a missing URL and a bare "Internal Server Error" when your code raises. Register handlers with @app.errorhandler to return your own templates instead. Return the template and the status code as a tuple, so the response still reports the right HTTP status:
from flask import render_template
@app.errorhandler(404)
def not_found(error):
return render_template("404.html"), 404
@app.errorhandler(500)
def server_error(error):
return render_template("500.html"), 500
The templates are ordinary pages, so extend your base layout and the navigation comes along, keeping the user oriented even on an error:
{% extends "base.html" %}
{% block title %}Page not found{% endblock %}
{% block content %}
That page does not exist
{% endblock %}
The interactive debugger
Running with debug=True turns a 500 error into an interactive traceback in the browser: the full stack, the line that failed, and a console you can expand at each frame. Your custom 500.html is what users see when debug mode is off. Debug mode also reloads the server automatically when you save a file, so you are not restarting by hand all day.
if __name__ == "__main__":
app.run(debug=True) # local development only
One firm rule: debug=True is for your machine only. The interactive console can run arbitrary code, so shipping it to a public server hands anyone who triggers an error a way into your system. Deployment (covered in Chapter 20) turns it off.
Read both consoles
A Flask app runs in two places at once, and each has its own console. Knowing which one to look at is half of debugging:
- The terminal running the server is where Python lives. Tracebacks,
print()output, and the request log (each URL and its status code) appear here. Anything wrong in a route, a query, or a template render shows up in the terminal. - The browser DevTools console (F12) is where the client lives. JavaScript errors, failed
fetchcalls, and missing static files (a 404 for your CSS) show up here, not in the terminal. When a page loads but looks or behaves wrong, this is the console to open.
When something misbehaves, the first question is simply "which console is complaining?" The answer usually points straight at whether the problem is server-side Python or client-side markup.
Common first-run errors
A handful of errors account for most early Flask trouble. Recognising them by their message saves the most time:
TemplateNotFound. The file is not intemplates/, or the name is misspelled. Flask only looks insidetemplates/by default, and the name is case-sensitive.- Your CSS does not load (404 on the stylesheet). The file is not under
static/, or the link is hardcoded and wrong. Useurl_for('static', ...)and check the DevTools network tab for the 404. RuntimeError: The session is unavailable.... You usedsessionorflash()without settingapp.secret_key. Set the key.sqlite3.OperationalError: no such table. The database file is missing, or the schema has not been created yet. This is the empty-state case the capstone guards against; initialise the Chapter 16 database and run a snapshot.Address already in use. A previous server is still running on the port. Stop it, or run on a different port withapp.run(port=5001).
None of these are mysterious once you have seen them. Keep the terminal visible while you work, and most of them announce themselves the moment they happen.