nodejs/node · error · TypeError

macro %r was invoked with two values for the special caller

Error message

macro %r was invoked with two values for the special caller argument.  This is most likely a bug.

What it means

Raised by Macro._invoke when the macro does not accept catch_kwargs (**kwargs) but the call passed a keyword argument literally named 'caller' while an explicit caller was already bound (self.caller is truthy and the special caller was appended to arguments). The runtime treats 'caller' as reserved for {% call %} blocks; receiving it twice means the template is double-passing the caller, almost always a Jinja2 bug rather than user intent.

Source

Thrown at tools/inspector_protocol/jinja2/runtime.py:564

                arguments.append(value)
        else:
            found_caller = self.explicit_caller

        # it's important that the order of these arguments does not change
        # if not also changed in the compiler's `function_scoping` method.
        # the order is caller, keyword arguments, positional arguments!
        if self.caller and not found_caller:
            caller = kwargs.pop('caller', None)
            if caller is None:
                caller = self._environment.undefined('No caller defined',
                                                     name='caller')
            arguments.append(caller)

        if self.catch_kwargs:
            arguments.append(kwargs)
        elif kwargs:
            if 'caller' in kwargs:
                raise TypeError('macro %r was invoked with two values for '
                                'the special caller argument.  This is '
                                'most likely a bug.' % self.name)
            raise TypeError('macro %r takes no keyword argument %r' %
                            (self.name, next(iter(kwargs))))
        if self.catch_varargs:
            arguments.append(args[self._argument_count:])
        elif len(args) > self._argument_count:
            raise TypeError('macro %r takes not more than %d argument(s)' %
                            (self.name, len(self.arguments)))

        return self._invoke(arguments, autoescape)

    def _invoke(self, arguments, autoescape):
        """This method is being swapped out by the async implementation."""
        rv = self._func(*arguments)
        if autoescape:
            rv = Markup(rv)
        return rv

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Stop forwarding 'caller' as a keyword argument: drop caller=... from the inner macro invocation.
  2. If the macro legitimately accepts catch_kwargs, filter out 'caller' before forwarding: pass **{k:v for k,v in kwargs.items() if k!='caller'}.
  3. Restructure so only the {% call %} block supplies caller; inner macros receive their own call blocks.
  4. Audit macro signatures and call sites for accidental double-supply of caller.

Example fix

// before
{% macro wrap(fn) %}{{ fn(caller=caller) }}{% endmacro %}
// after
{% macro wrap(fn) %}{{ fn() }}{% endmacro %}
{# let the inner {% call %} block define its own caller #}
Defensive patterns

Strategy: validation

Validate before calling

# Static scan: a {% call %} block whose body forwards caller=... into a macro
# is the common trigger. Flag forwarding of caller kwarg.
import re, pathlib
for p in pathlib.Path('templates').rglob('*.html'):
    src = p.read_text()
    if re.search(r'\{%\s*call[^%]*%\}.*caller\s*=', src, re.S):
        print(f'{p}: possible double-supply of caller')

Type guard

def forwards_caller_kwarg(macro_call_src: str) -> bool:
    return 'caller=' in macro_call_src

Try / catch

# runtime TypeError is a bug; not recoverable. Fix the template.

Prevention

When it happens

Trigger: A {% call %} block wraps a macro and the macro body itself forwards caller=... as a keyword argument to another invocation of the same macro. Manually invoking a macro with caller=something while it is already being called via {% call %}. Programmatic macro.call(..., caller=...) when the macro already received a caller from a {% call %} wrapper.

Common situations: Macro composition where a wrapper macro forwards kwargs blindly (catch_kwargs=True) into an inner macro that also uses {% call %}. Refactoring call blocks into helper macros and accidentally passing caller through. Async/await macro shims that re-dispatch arguments.

Related errors


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