Flask Troubleshooting

Introduction

Flask errors often look cryptic until you know the pattern—wrong virtual environment, missing template, CSRF failure, or Nginx returning 502 while Gunicorn is down. This chapter maps common symptoms to causes and fixes, with links back to the chapters where each topic is taught in depth.

Prerequisites

Diagnostic Flow

text
Error message → classify (install / code / DB / deploy) → check logs → fix one layer at a time
LayerWhere to look
Local devTerminal running flask run
Gunicornjournalctl -u flask-app, error log file
Nginx/var/log/nginx/error.log
DatabaseMySQL error log, app traceback

Installation and Import Errors

ModuleNotFoundError: No module named 'flask'

Cause: Virtual environment not activated, or Flask installed globally while you run another Python.

Fix:

bash
source .venv/bin/activate
pip install -r requirements.txt
which python
python -c "import flask; print(flask.__version__)"

See Flask Environment and Installation.

ImportError: cannot import name 'create_app'

Cause: Circular import, wrong package path, or FLASK_APP pointing at wrong module.

Fix:

  • Import blueprints inside create_app, not at top of __init__.py
  • Use extensions.py for db, login_manager
  • Set FLASK_APP=app:create_app or wsgi:app

See Blueprints and Application Factory.

Server and Port Issues

Address already in use (Port 5000 or 8000)

Cause: Previous flask run or Gunicorn still bound to the port.

Fix:

bash
# macOS / Linux
lsof -i :5000
kill <PID>
 
# Or use another port
flask run --port 5001

On Linux servers:

bash
ss -tlnp | grep 8000

flask: command not found

Cause: venv not active or Flask not installed in current env.

Fix: Activate venv; pip install Flask.

Templates and Static Files

jinja2.exceptions.TemplateNotFound

Cause: Wrong path, typo in render_template('blog/index.html'), or templates not next to app root.

Fix:

  • Confirm templates/ folder exists
  • Factory app: Flask(__name__) where __name__ is package app
  • Blueprint: set template_folder if templates live inside blueprint package

See Jinja2 Templates.

CSS/JS 404 in browser

Cause: Wrong url_for('static', filename=...), file not in static/, or Nginx alias path wrong in production.

Fix:

  • Dev: file under app/static/css/style.css
  • Prod: Nginx location /static/ alias matches deploy path—deployment chapter

Forms and CSRF

CSRF token missing / The CSRF token is invalid

Cause: Form POST without {{ form.hidden_tag() }} or missing SECRET_KEY.

Fix:

html
<form method="post">
  {{ form.hidden_tag() }}
  ...
</form>
python
app.config["SECRET_KEY"] = "stable-dev-key"

Tests may disable WTF_CSRF_ENABLED in TestingConfig only.

See Forms and Flask-WTF.

request.form empty on POST

Cause: Missing method="post", wrong field name, or reading request.json instead of form.

See The Request Object.

Database Errors

sqlalchemy.exc.OperationalError: connection refused

Cause: MySQL not running, wrong host/port, firewall, or bad DATABASE_URL.

Fix:

bash
sudo systemctl status mysql
mysql -u flask -p -h 127.0.0.1 flask_app

Check URI:

text
mysql+pymysql://user:password@127.0.0.1:3306/dbname?charset=utf8mb4

See Flask-SQLAlchemy and Models and MySQL troubleshooting.

Unknown column / no such table

Cause: Model changed but migration not applied.

Fix:

bash
flask db migrate -m "describe change"
flask db upgrade

Never rely on db.create_all() alone in production—Flask-Migrate chapter.

Garbled Unicode (emoji, Chinese)

Cause: MySQL charset not utf8mb4.

Fix: Database, table, and connection string charset—MySQL configuration.

Authentication

Redirect loop on login

Cause: Login view has @login_required, or login_view points to wrong endpoint.

Fix: Login route must be public; login_manager.login_view = "auth.login" must match blueprint endpoint name.

See Flask-Login chapter.

401 on API with JWT

Cause: Missing Authorization: Bearer <token>, expired token, or wrong JWT_SECRET_KEY between restarts.

See JWT chapter.

CORS (Browser Only)

Access-Control-Allow-Origin blocked

Cause: SPA on different port; Flask did not send CORS headers.

Fix: Flask-CORS for /api/*, or Vite dev proxy—CORS chapter.

Note: curl and Postman ignore CORS—if they work but browser fails, it is CORS.

Production and Nginx

502 Bad Gateway

Cause: Gunicorn not running, wrong proxy_pass port, or socket permissions.

Fix:

bash
sudo systemctl status flask-app
curl http://127.0.0.1:8000/health
sudo nginx -t

Match Nginx proxy_pass http://127.0.0.1:8000 to Gunicorn bind address.

See Deploying With Gunicorn and Nginx.

504 Gateway Timeout

Cause: Slow view, worker timeout, or Nginx proxy_read_timeout too low.

Fix: Optimize query; increase Gunicorn --timeout and Nginx read timeout.

App works locally, fails on server

Checklist:

  • DEBUG=False but secrets set in env file
  • Migrations applied on server
  • Same Python version and requirements.txt pinned
  • File permissions (www-data owns app dir)

Session resets every request

Cause: SECRET_KEY changes on each deploy without stable env var.

Fix: Persistent SECRET_KEY in /etc/flask-app/env.

Debugger PIN exposed

Cause: DEBUG=True on public server.

Fix: Turn off debug immediately—security chapter.

Quick Reference Table

SymptomLikely causeFirst action
No module flaskvenvActivate + pip install
TemplateNotFoundpath/nameCheck templates/ tree
CSRF errormissing tokenhidden_tag()
502Gunicorn downsystemctl status
Static 404Nginx aliasFix location /static/
DB connectionMySQL/URITest mysql CLI
CORS browserheadersFlask-CORS or proxy
Circular importfactoryLazy blueprint import

FAQ

Where is the full traceback?

Dev: terminal or browser debug page. Prod: Gunicorn error log + app.logger.exception.

Enable Flask debug temporarily?

Only on localhost—never on 0.0.0.0 facing internet.

pytest passes, manual test fails?

Different config—confirm TestingConfig vs DevelopmentConfig.

Docker container exits immediately?

docker logs <container>—often missing env var or migration failure in CMD.

Still stuck?

Minimal repro: one route + one template; add complexity back until it breaks—bisect the problem.

Report doc typo?

Contribute via Git fork workflow.