The go-to resource for upgrading Python, Django, Flask, and your dependencies.

How to Fix ImportError: cannot import name 'escape' from 'jinja2' in Flask 2.3


If you’ve upgraded to Flask 2.3 and run into ImportError: cannot import name ‘escape’ from ‘jinja2’, let’s figure out what’s going on.

Flask 2.3 requires Jinja2 >=3.1. Jinja2 removed escape from its API — it’s now in MarkupSafe — to encourage using template filters over direct imports.

This affects custom helpers or extensions that imported escape directly. Why update? It aligns with Jinja’s design for safer, more maintainable templating.

Why This Error Occurs

The Jinja2 3.1 changelog states: “Markup and escape should be imported from MarkupSafe.”

You might wonder why — it separates escaping logic (MarkupSafe) from templating (Jinja2), promoting better modularity.

Common in:

  • Custom template helpers.
  • Extensions like Flask-Admin, Flask-Bootstrap.

Full error:

ImportError: cannot import name 'escape' from 'jinja2' (/path/to/jinja2/__init__.py)
CauseFlask VersionJinja2 Version
Direct from jinja2 import escape>=2.3>=3.1

Reproducing the Error

# app.py
from flask import Flask
from jinja2 import escape  # <- Breaks here!

app = Flask(__name__)

@app.route('/')
def index():
    return escape('<script>alert(1)</script>')  # Example usage

if __name__ == '__main__':
    app.run(debug=True)

Run these commands to reproduce:

$ pip install flask==2.3.0
$ python app.py

You’ll see the ImportError.

Of course, reproducing helps us understand the issue before fixing it.

Solution 1: Update the Import

We’ll update the import:

# OLD (broken)
from jinja2 import escape

# NEW (fixed)
from markupsafe import escape

Full fixed app.py:

from flask import Flask
from markupsafe import escape  # Fixed import

app = Flask(__name__)

@app.route('/')
def index():
    safe_html = escape('<script>alert(1)</script>')
    return f'<h1>Safe: {safe_html}</h1>'

if __name__ == '__main__':
    app.run(debug=True)

Test: python app.py — the app starts without error.

Solution 2: Downgrade Jinja2 (Temporary)

Pin older Jinja2:

pip install "jinja2<3.1"
ProsCons
Quick fix, no code changesSecurity risks from older Jinja2; blocks Flask/Jinja2 updates; not sustainable long-term

We generally avoid this in production — it trades short-term convenience for long-term maintenance issues.

Update Extensions

Scan code:

grep -r "from jinja2 import escape" src/

Common fixes:

  • Flask-Admin: Update to >=1.6.1.
  • Flask-Bootstrap: Update to >=3.4.1.

Verify Fix

  1. pip list | grep -E 'flask|jinja2|markupsafe'
  2. Run tests/app.
  3. Check templates use |e filter: {{ user_input | e }}.

Best Practices for Flask + Jinja2

  • Avoid direct imports: Use Jinja |e (escape) filter.
  • Jinja2 filters:
    {{ user_input | e }}     # HTML escape
    {{ user_input | safe }}  # Bypass (use sparingly!)
  • requirements.txt:
    Flask==2.3.0
    Jinja2>=3.1
    MarkupSafe>=2.1
  • Pre-commit hook for imports:
    # .pre-commit-config.yaml
    repos:
    - repo: local
      hooks:
      - id: check-jinja-imports
        entry: grep -r "from jinja2 import escape"
  • CI/CD: Add pip-audit (see post 23).

Conclusion

Update to from markupsafe import escape for Flask 2.3 + Jinja2 3.1 compatibility. Prefer template filters for escaping.

Fixed your app? Share your setup! See related: Flask dependency audits.

Sponsored by Durable Programming

Need help maintaining or upgrading your Python application? Durable Programming specializes in keeping Python apps secure, performant, and up-to-date.

Hire Durable Programming