nodejs/node · error · TemplateNotFound

{template}

Error message

{template}

What it means

split_template_path segments a template name on '/' and rejects any segment containing an OS path separator or equal to the parent-directory marker ('..'). This is a security guard against directory traversal: it raises TemplateNotFound(template) before the loader ever touches the filesystem.

Source

Thrown at tools/inspector_protocol/jinja2/loaders.py:31

import weakref
from types import ModuleType
from os import path
from hashlib import sha1
from jinja2.exceptions import TemplateNotFound
from jinja2.utils import open_if_exists, internalcode
from jinja2._compat import string_types, iteritems


def split_template_path(template):
    """Split a path into segments and perform a sanity check.  If it detects
    '..' in the path it will raise a `TemplateNotFound` error.
    """
    pieces = []
    for piece in template.split('/'):
        if path.sep in piece \
           or (path.altsep and path.altsep in piece) or \
           piece == path.pardir:
            raise TemplateNotFound(template)
        elif piece and piece != '.':
            pieces.append(piece)
    return pieces


class BaseLoader(object):
    """Baseclass for all loaders.  Subclass this and override `get_source` to
    implement a custom loading mechanism.  The environment provides a
    `get_template` method that calls the loader's `load` method to get the
    :class:`Template` object.

    A very basic example for a loader that looks up templates on the file
    system could look like this::

        from jinja2 import BaseLoader, TemplateNotFound
        from os.path import join, exists, getmtime

        class MyLoader(BaseLoader):

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Use only forward-slash-relative names within the configured template root; never include '..' in a template name.
  2. Sanitize/normalize user-supplied names before passing them to get_template (reject '..' and path separators).
  3. If you need templates outside the root, add the directory to the loader's searchpath explicitly.

Example fix

# before
env.get_template('../shared/header.html')

# after
loader = FileSystemLoader(['/app/templates', '/app/shared'])
env.get_template('header.html')
Defensive patterns

Strategy: validation

Validate before calling

import os

def safe_template_name(name: str) -> str:
    if not name or name != name.strip():
        raise ValueError('template name must be non-empty and trimmed')
    for piece in name.split('/'):
        if piece in ('', '.', '..') or os.sep in piece or (os.altsep and os.altsep in piece):
            raise ValueError('unsafe template name: %r' % name)
    return name

Type guard

import os

def is_safe_template_name(name: str) -> bool:
    if not isinstance(name, str) or not name:
        return False
    for piece in name.split('/'):
        if piece in ('', '.', '..') or os.sep in piece or (os.altsep and os.altsep in piece):
            return False
    return True

Try / catch

from jinja2 import TemplateNotFound
try:
    tmpl = env.get_template(name)
except TemplateNotFound:
    raise ValueError('template not found or name rejected: %r' % name)

Prevention

When it happens

Trigger: Calling get_template('../secret'), get_template('foo/../../etc/passwd'), or passing a name with a backslash segment on a system where altsep applies — any name that could escape the loader's root.

Common situations: Building template names from user input without sanitization; cross-platform path strings using '\\' on POSIX; attempting to share templates outside the configured template root.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/92385900f3e3195d. Report an issue: GitHub.