nodejs/node · error · TemplateRuntimeError

cannot assign attribute on non-namespace object

Error message

cannot assign attribute on non-namespace object

What it means

Compiled-in runtime guard emitted by visit_NSRef when a template uses namespace attribute assignment (`{% set ns.foo = ... %}`) but the target is not a Jinja2 Namespace object at render time. NSRef nodes are emitted for `set x.y = value`; before performing the assignment the generated code asserts isinstance(ref, Namespace).

Source

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

        # instruction indicates a parameter which are always defined.
        if node.ctx == 'load':
            load = frame.symbols.find_load(ref)
            if not (load is not None and load[0] == VAR_LOAD_PARAMETER and \
                    not self.parameter_is_undeclared(ref)):
                self.write('(undefined(name=%r) if %s is missing else %s)' %
                           (node.name, ref, ref))
                return

        self.write(ref)

    def visit_NSRef(self, node, frame):
        # NSRefs can only be used to store values; since they use the normal
        # `foo.bar` notation they will be parsed as a normal attribute access
        # when used anywhere but in a `set` context
        ref = frame.symbols.ref(node.name)
        self.writeline('if not isinstance(%s, Namespace):' % ref)
        self.indent()
        self.writeline('raise TemplateRuntimeError(%r)' %
                       'cannot assign attribute on non-namespace object')
        self.outdent()
        self.writeline('%s[%r]' % (ref, node.attr))

    def visit_Const(self, node, frame):
        val = node.as_const(frame.eval_ctx)
        if isinstance(val, float):
            self.write(str(val))
        else:
            self.write(repr(val))

    def visit_TemplateData(self, node, frame):
        try:
            self.write(repr(node.as_const(frame.eval_ctx)))
        except nodes.Impossible:
            self.write('(Markup if context.eval_ctx.autoescape else identity)(%r)'
                       % node.data)

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Initialize with `{% set ns = namespace(...) %}` before any `{% set ns.x = ... %}`.
  2. If you intended a dict, use bracket access (`{% set _ = mydict.update(...) %}`) rather than attribute-set syntax.
  3. Make sure the name used on the left of the dot is the same one bound to namespace().

Example fix

{# before #}
{% set item.count = item.count + 1 %}
{# after #}
{% set item = namespace(count=0) %}
{% set item.count = item.count + 1 %}
Defensive patterns

Strategy: validation

Validate before calling

from jinja2 import Namespace
def ensure_namespace(var):
    return var if isinstance(var, Namespace) else Namespace()

Type guard

from jinja2 import Namespace
def is_namespace(v) -> bool:
    return isinstance(v, Namespace)

Prevention

When it happens

Trigger: Writing `{% set myvar.attr = 1 %}` where myvar was never initialized as a Namespace (it is a plain dict, a string, None, or undefined); shadowing a namespace variable with a non-namespace value partway through a template.

Common situations: Forgetting the `{% set ns = namespace() %}` initialization before `{% set ns.counter = 0 %}`; expecting dicts to support attribute assignment; using namespace mutation to carry state across loop iterations but reassigning ns to something else first.

Related errors


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