Flask 2.x to 3.x Migration Guide — Breaking Changes

5 min read · Updated April 2026 · Safe version: Flask 3.0.3+ with Werkzeug 3.0.6+

Safe versions

Flask 3.0.3+ with Werkzeug 3.0.6+ — always update both together. Flask 3.0.3 requires Werkzeug 3.0.3 minimum.

Why upgrade from Flask 2.x?

Flask 2.3.1 and below have CVE-2023-30861 (session cookie leakage). Flask 2.3.2 patches it but Werkzeug 2.x has its own DoS CVEs (CVE-2024-49767, CVE-2023-46136). The safest path is upgrading both to 3.x.

Breaking changes — Flask 2.x to 3.x

1. Python 3.8 minimum

Flask 3.x requires Python 3.8 or later. Check your Python version first.

python --version  # Must be 3.8+

2. Removed deprecated Markup auto-escape

# Flask 2.x — worked but deprecated
from markupsafe import Markup
value = Markup("<b>safe</b>")

# Flask 3.x — same, but old import paths removed
# Use markupsafe directly, not flask.Markup

3. Before/after request handlers — return value change

# Flask 2.x
@app.before_request
def before():
    pass  # return None was implicit

# Flask 3.x — explicit None if not returning response
@app.before_request
def before():
    if not authenticated:
        return redirect('/login')
    return None  # explicit

4. CLI commands — click 8.0+ required

Flask 3.x requires click 8.0 or later. Update click alongside Flask.

pip install flask --upgrade click --upgrade

5. Werkzeug must be updated together

Always update together

Never update Flask without also updating Werkzeug. Mismatched versions cause import errors and unexpected behaviour.

# Always update both at once
pip install flask --upgrade werkzeug --upgrade

# Pin in requirements.txt
flask>=3.0.3
werkzeug>=3.0.6

Common errors when upgrading

ImportError: cannot import name 'Markup' from 'flask'

ImportError: cannot import name 'Markup' from 'flask'

Fix: import Markup from markupsafe directly.

# Before
from flask import Markup

# After
from markupsafe import Markup

RuntimeError: The current Flask app is not registered

RuntimeError: Working outside of application context.

This error exists in both 2.x and 3.x but is more strictly enforced in 3.x. Always use app.app_context() when accessing app outside of request context.

with app.app_context():
    # your code here
    db.session.query(User).all()

Werkzeug version mismatch

ImportError: cannot import name 'url_quote' from 'werkzeug.urls'

Caused by Flask 3.x installed with old Werkzeug 2.x. Fix: update Werkzeug.

pip install werkzeug --upgrade

Version compatibility table

Flask Werkzeug Python Status
3.0.3+3.0.6+3.8+ SAFE
2.3.22.3.x3.7+ OUTDATED
2.3.1 and belowanyany VULNERABLE

Related

After updating - verify your fix

Run these commands to confirm the update worked:

# npm projects
npm list multer
npm list node-fetch

# Python projects
pip show fastapi | grep Version
pip show flask | grep Version

# Scan your full manifest for other vulnerabilities
# Paste your requirements.txt or package.json into PackageFix

Paste your full manifest into PackageFix to check all packages at once.

Scan your full manifest — PackageFix checks all 7 ecosystems against OSV and CISA KEV.

Scan with PackageFix →

Free · No signup · No CLI required

Vulnerability data sourced from the OSV database and public package registries. Always test dependency updates in a staging environment before deploying to production. PackageFix provides these tools for informational purposes only and cannot guarantee that pinned versions are free from undiscovered vulnerabilities.