Skip to main content

How to Resolve Python Jinja2 Error "ImportError: cannot import name 'escape'" (or 'Markup')

When working with Flask, or other Python libraries that utilize the Jinja2 templating engine, you might encounter ImportError exceptions like cannot import name 'escape' from 'jinja2' or cannot import name 'Markup' from 'jinja2'. These errors typically appear after updating packages and signify that the way escape and Markup are imported has changed in recent Jinja2 versions. Specifically, they were moved to the markupsafe library, a dependency of Jinja2.

This guide explains this change (introduced in Jinja2 v3.1.0) and provides the standard solutions to resolve these import errors.

Understanding the Error: Relocation of escape and Markup

  • escape: A function used to safely escape characters in a string for HTML output, preventing cross-site scripting (XSS) vulnerabilities.
  • Markup: A class representing a string that is already considered "safe" for HTML output and should not be automatically escaped further by the templating engine.
  • markupsafe: A library depended upon by Jinja2 that provides the core implementation for HTML-safe string handling, including escape and Markup.

The Change in Jinja2 v3.1.0:

  • Before v3.1.0: Jinja2 re-exported escape and Markup directly from its own top-level namespace (jinja2.escape, jinja2.Markup). Code could import them directly from jinja2.
  • From v3.1.0 Onwards: Jinja2 stopped re-exporting these names directly. The canonical location to import them from is now the underlying markupsafe library.

The ImportError occurs when code (either yours or a library like an older Flask version) tries to use the old import path (from jinja2 import escape) while running with Jinja2 version 3.1.0 or newer.

Cause: Using Old Import Path with Jinja2 v3.1.0+

The direct cause is an import statement like from jinja2 import escape or from jinja2 import Markup executing in an environment where Jinja2's version is 3.1.0 or greater.

error_scenario.py
# (running with Jinja2 >= 3.1.0)
try:
# ⛔️ ImportError: cannot import name 'escape' from 'jinja2'
from jinja2 import escape
print("Imported escape from jinja2 (unexpected).")
except ImportError as e:
print(f"Caught expected error for escape: {e}")

try:
# ⛔️ ImportError: cannot import name 'Markup' from 'jinja2'
from jinja2 import Markup
print("Imported Markup from jinja2 (unexpected).")
except ImportError as e:
print(f"Caught expected error for Markup: {e}")

If the error originates from a library you are using (most commonly Flask), the best solution is usually to upgrade that library. Newer versions of frameworks like Flask (specifically Flask v2.0.0 and later) have updated their internal code to import escape and Markup from the correct markupsafe location.

# Upgrade Flask (this often pulls in compatible Jinja2 and Werkzeug)
pip install --upgrade Flask

# Or use pip3 / python -m pip etc.
pip3 install --upgrade Flask
python -m pip install --upgrade Flask

After upgrading Flask (or other relevant libraries like Dash, etc.), the internal import errors should be resolved because the library now uses the correct import paths compatible with newer Jinja2 versions.

Solution 2: Pin Jinja2 Version to < 3.1.0 (Workaround)

If you cannot upgrade the dependent library (e.g., due to compatibility constraints with other parts of your project) or if the problematic import is in code you cannot easily change, you can force pip to install the last version of Jinja2 that did export escape and Markup directly. This is version 3.0.3.

Installing Older Jinja2 via Pip

# Uninstall current Jinja2 (optional but clean)
pip uninstall Jinja2

# Install the specific older version (use --force-reinstall if needed)
pip install Jinja2==3.0.3 --force-reinstall

# Or use pip3 / python -m pip etc.
pip3 install Jinja2==3.0.3 --force-reinstall
python -m pip install Jinja2==3.0.3 --force-reinstall
  • --force-reinstall: Ensures that version 3.0.3 is installed even if a different version is already present or cached.

Pinning in requirements.txt

Add this line to your requirements.txt to ensure consistent installation across environments:

# requirements.txt
# ... other dependencies ...
Jinja2==3.0.3
# Werkzeug==2.0.3 # Often needed together for older Flask compatibility
# ... other dependencies ...

Then run pip install -r requirements.txt.

note

Pinning prevents you from getting newer Jinja2 features and bug fixes. Upgrading dependent libraries (Solution 1) is the preferred long-term fix.

Solution 3: Update Your Code's Import Statements

If the ImportError is occurring in your own application code, modify your import statements to use markupsafe directly.

# Remove the old, incorrect import:
# from jinja2 import escape, Markup # ⛔️ REMOVE THIS

# ✅ Add the correct import from markupsafe:
from markupsafe import escape, Markup

# Now you can use escape() and Markup() as before
html_snippet = "<script>alert('xss');</script>"
safe_html = escape(html_snippet)
already_safe = Markup("<strong>This is safe</strong>")

print(f"Original: {html_snippet}")
print(f"Escaped: {safe_html}")
print(f"Markup object: {already_safe}")

This is the correct approach for your own code to be compatible with Jinja2 v3.1.0+ and reflects the library's structure.

note

Ensure markupsafe is installed (usually comes with Jinja2/Flask)

pip install --upgrade markupsafe

General Troubleshooting: Upgrade All Packages

In complex environments, conflicts might arise from multiple outdated packages. While sometimes risky (it can break compatibility), upgrading all packages might resolve transitive dependency issues.

  • Method 1: Using pip-review (install first: pip install pip-review)

    pip-review --auto
  • Method 2: Script (use with extreme caution - test thoroughly!)

    import pkg_resources
    from subprocess import call
    packages = [dist.project_name for dist in pkg_resources.working_set]
    print(f"Attempting to upgrade: {' '.join(packages)}")
    call("pip install --upgrade " + ' '.join(packages), shell=True)

Recommendation: Prefer upgrading key frameworks (Flask, Dash) first (Solution 1). Only resort to upgrading everything if specific upgrades fail and you understand the potential risks. Always use a virtual environment.

Verifying Versions (pip show)

After applying a solution, check the installed versions:

pip show Jinja2
pip show markupsafe
pip show Flask # Or other relevant dependent library
note

Ensure the versions match what you expect based on the solution you applied (e.g., Jinja2 pinned at 3.0.3, or Flask updated to >= 2.0).

Conclusion

The ImportError: cannot import name 'escape' or 'Markup' from 'jinja2' is caused by API changes in Jinja2 version 3.1.0, where these utilities were moved solely to the markupsafe library.

The primary solutions are:

  1. Upgrade Dependent Libraries: Update frameworks like Flask (to v2.0+) or Dash, which have adapted to the new import locations. This is the preferred solution.
  2. Pin Jinja2: If dependencies cannot be upgraded, pin Jinja2 to 3.0.3 (pip install Jinja2==3.0.3) or add Jinja2==3.0.3 to requirements.txt. This is a workaround.
  3. Fix Your Own Imports: If the error is in your code, change from jinja2 import escape to from markupsafe import escape (similarly for Markup).

Using virtual environments helps manage these dependencies effectively and avoid conflicts.