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
- Keep exactly one {% extends %} per template, at the top.
- Replace the second inheritance need with {% include %} or macro imports ({% from %} / {% import %}).
- 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
- Keep exactly one {% extends %} per template, at the top.
- Use {% include %} / macros / {% import %} for composition instead of multiple extends.
- Lint templates in CI to reject duplicate extends tags.
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
- Template module attribute is unavailable in async mode
- Loop length for some iterators cannot be lazily calculated i
- cannot assign attribute on non-namespace object
- no loader for this environment specified
- Tried to select from an empty list of templates.
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/87d94d9b6d8586e2.
Report an issue: GitHub.