Debugging Template Loading
When you encounter a TemplateNotFound error or find that the wrong template is being rendered, Flask provides a built-in debugging tool to trace exactly how it resolves template paths. Because Flask searches for templates in the application's folder first and then in every registered blueprint's folder, identifying which loader is winning (or why all are failing) can be difficult without visibility into the search process.
Enabling Template Loading Explanations
To see the sequence of attempts Flask makes to find a template, set the EXPLAIN_TEMPLATE_LOADING configuration option to True.
from flask import Flask
app = Flask(__name__)
app.config["EXPLAIN_TEMPLATE_LOADING"] = True
When this is enabled, every request that involves template rendering will log the resolution attempts to the application logger at the INFO level.
Interpreting the Resolution Log
When you attempt to render a template, the DispatchingJinjaLoader iterates through all available loaders. If EXPLAIN_TEMPLATE_LOADING is active, it uses explain_template_loading_attempts from flask.debughelpers to format the results.
Example: Missing Template
If you try to render missing.html and it cannot be found, your logs will look like this:
[INFO] Locating template 'missing.html':
1: trying loader of application 'my_app'
class: jinja2.loaders.FileSystemLoader
searchpath:
- /path/to/app/templates
-> no match
2: trying loader of blueprint 'auth' (my_app.auth)
class: jinja2.loaders.FileSystemLoader
searchpath:
- /path/to/app/blueprints/auth/templates
-> no match
Error: the template could not be found.
Example: Template Shadowing
Flask searches the application's template folder before any blueprint folders. If a blueprint and the application both have a template with the same name, the application's version will always be used. The debug log identifies this "shadowing" by warning you when multiple loaders find a match:
[INFO] Locating template 'layout.html':
1: trying loader of application 'my_app'
class: jinja2.loaders.FileSystemLoader
searchpath:
- /path/to/app/templates
-> found ('/path/to/app/templates/layout.html')
2: trying loader of blueprint 'admin' (my_app.admin)
class: jinja2.loaders.FileSystemLoader
searchpath:
- /path/to/app/blueprints/admin/templates
-> found ('/path/to/app/blueprints/admin/templates/layout.html')
Warning: multiple loaders returned a match for the template.
Resolution Order
The DispatchingJinjaLoader follows a strict order when searching for templates:
- Application Loader: The
jinja_loaderdefined on theFlaskapp instance (usually looking in the/templatesfolder). - Blueprint Loaders: Loaders for all registered blueprints, searched in the order they were registered via
app.register_blueprint().
This order is implemented in DispatchingJinjaLoader._iter_loaders:
def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]:
loader = self.app.jinja_loader
if loader is not None:
yield self.app, loader
for blueprint in self.app.iter_blueprints():
loader = blueprint.jinja_loader
if loader is not None:
yield blueprint, loader
Troubleshooting
Logs are not appearing
The template loading explanation is logged using app.logger.info. If your logging level is set to WARNING or ERROR, these messages will be suppressed even if EXPLAIN_TEMPLATE_LOADING is True. Ensure your logger is configured to show INFO messages:
import logging
logging.basicConfig(level=logging.INFO)
Blueprint templates not found
If the logs show that a blueprint's loader is being skipped or is not searching the path you expect, verify that:
- The blueprint was initialized with a
template_folderargument (e.g.,Blueprint("auth", __name__, template_folder="templates")). - The blueprint has been registered on the
appinstance.DispatchingJinjaLoaderonly iterates over blueprints returned byapp.iter_blueprints(). - The template is located within the specified folder. If you are using a blueprint named
authand your template is atauth/login.html, the file should be atblueprints/auth/templates/auth/login.html.