pallets/flask · error · TemplateNotFound
{template}
Error message
{template} What it means
A `jinja2.TemplateNotFound` raised by Flask's DispatchingJinjaLoader in `_get_source_explained` (used when `EXPLAIN_TEMPLATE_LOADING=True`). Flask tried every loader — the app's `templates/` folder and each registered blueprint's template folder — and none could supply the named template. In this explain path, Flask first logs a detailed report of every loader attempted before raising.
Source
Thrown at src/flask/templating.py:86
rv: tuple[str, str | None, t.Callable[[], bool] | None] | None
trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None
for srcobj, loader in self._iter_loaders(template):
try:
rv = loader.get_source(environment, template)
if trv is None:
trv = rv
except TemplateNotFound:
rv = None
attempts.append((loader, srcobj, rv))
from .debughelpers import explain_template_loading_attempts
explain_template_loading_attempts(self.app, template, attempts)
if trv is not None:
return trv
raise TemplateNotFound(template)
def _get_source_fast(
self, environment: BaseEnvironment, template: str
) -> tuple[str, str | None, t.Callable[[], bool] | None]:
for _srcobj, loader in self._iter_loaders(template):
try:
return loader.get_source(environment, template)
except TemplateNotFound:
continue
raise TemplateNotFound(template)
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_loaderView on GitHub ↗ (pinned to 6a2f545bfd)
Solutions
- Check the explain-template-loading log output just emitted — it lists every loader and path Flask searched; put the file where one of those paths points.
- Fix the filename/case in `render_template()` to exactly match the file on disk.
- Pass `template_folder='templates'` when constructing the Blueprint if the template lives with the blueprint.
- For installed packages, ensure templates are included in the distribution (package_data or MANIFEST.in).
Example fix
# before
bp = Blueprint("admin", __name__)
render_template("admin/Index.html")
# after
bp = Blueprint("admin", __name__, template_folder="templates")
render_template("admin/index.html") # matches on-disk case Defensive patterns
Strategy: try-catch
Validate before calling
import os tpl = 'user/profile.html' exists = os.path.exists(os.path.join(app.root_path, app.template_folder, tpl))
Type guard
def template_exists(app, name):
return app.jinja_env.get_or_select_template is not None and name in app.jinja_env.list_templates() Try / catch
from jinja2 import TemplateNotFound
try:
return render_template('page.html')
except TemplateNotFound:
abort(404) Prevention
- Never build template names from user input; if you must, whitelist against app.jinja_env.list_templates()
- Keep templates in the folder matching template_folder and mind blueprint template search order
- Run the app with EXPLAIN_TEMPLATE_LOADING=True when debugging which loader/path was searched
- Catch jinja2.TemplateNotFound at route level and return a proper 404 rather than a 500
When it happens
Trigger: `render_template('name.html')` (or `render_template_string` extending a missing template, `{% include %}`/`{% extends %}` of a missing file) when the file doesn't exist in the app's template_folder or any blueprint's template_folder, with EXPLAIN_TEMPLATE_LOADING enabled.
Common situations: Typo or wrong case in the template filename (case-sensitive on Linux, not on macOS/Windows dev machines); templates placed next to the module instead of in `templates/`; blueprint created without `template_folder=` set; package installed without including template files (missing package_data/MANIFEST.in); wrong `template_folder` path relative to the app root.
Related errors
- Working outside of application context. Attempted to use fu
- Working outside of request context. Attempted to use functi
- The session is unavailable because no secret key was set. S
- The environment variable {variable_name!r} is not set and as
- 'after_this_request' can only be used when a request context
AI-assisted analysis of pallets/flask@6a2f545bfd (2026-07-31).
Data as JSON: /data/errors/92f2922247de286a.json.
Report an issue: GitHub ↗.