nodejs/node · error · TemplateRuntimeError

extended multiple times

Error message

extended multiple times

What it means

Emitted by the compiler when a template contains more than one {% extends %} statement. A template may inherit from at most one parent; the second extends trips an extends_so_far > 0 check. When the parent is statically known the compiler aborts early via CompilerExit; otherwise it emits a runtime TemplateRuntimeError('extended multiple times') into the generated code.

Source

Thrown at tools/inspector_protocol/jinja2/compiler.py:861

    def visit_Extends(self, node, frame):
        """Calls the extender."""
        if not frame.toplevel:
            self.fail('cannot use extend from a non top-level scope',
                      node.lineno)

        # if the number of extends statements in general is zero so
        # far, we don't have to add a check if something extended
        # the template before this one.
        if self.extends_so_far > 0:

            # if we have a known extends we just add a template runtime
            # error into the generated code.  We could catch that at compile
            # time too, but i welcome it not to confuse users by throwing the
            # same error at different times just "because we can".
            if not self.has_known_extends:
                self.writeline('if parent_template is not None:')
                self.indent()
            self.writeline('raise TemplateRuntimeError(%r)' %
                           'extended multiple times')

            # if we have a known extends already we don't need that code here
            # as we know that the template execution will end here.
            if self.has_known_extends:
                raise CompilerExit()
            else:
                self.outdent()

        self.writeline('parent_template = environment.get_template(', node)
        self.visit(node.template, frame)
        self.write(', %r)' % self.name)
        self.writeline('for name, parent_block in parent_template.'
                       'blocks.%s():' % dict_item_iter)
        self.indent()
        self.writeline('context.blocks.setdefault(name, []).'
                       'append(parent_block)')
        self.outdent()

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Keep exactly one {% extends %} per template, at the top.
  2. Replace the second inheritance need with {% include %} or macro imports ({% from %} / {% import %}).
  3. If extends must be conditional, use a single {% extends % %} with a computed parent expression rather than multiple extends tags.

Example fix

{# before #}
{% extends "base.html" %}
{% extends "other.html" %}
{# after #}
{% extends "base.html" %}
{% from "other.html" import extra_block %}
Defensive patterns

Strategy: validation

Validate before calling

from jinja2 import nodes
def count_extends(template_node) -> int:
    n = 0
    for stmt in template_node.body:
        if isinstance(stmt, nodes.Extends):
            n += 1
    return n
# assert count_extends(parsed) <= 1

Prevention

When it happens

Trigger: Authoring a template with two {% extends "x" %} tags; conditionally including an extends inside a macro/include that itself extends; concatenating template fragments that each carry their own extends.

Common situations: Copy-pasting a header (with its extends) into a base template that already extends; building a template dynamically from parts where each part brings an extends; misunderstanding multi-inheritance in Jinja2 (use {% include %} / macros instead).

Related errors


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