{"record":{"id":"35327c4138f2cec0","repo":"nodejs/node","slug":"macro-r-was-invoked-with-two-values-for-the-speci","errorCode":null,"errorMessage":"macro %r was invoked with two values for the special caller argument.  This is most likely a bug.","messagePattern":"macro %r was invoked with two values for the special caller argument\\.  This is most likely a bug\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"tools/inspector_protocol/jinja2/runtime.py","lineNumber":564,"sourceCode":"                arguments.append(value)\n        else:\n            found_caller = self.explicit_caller\n\n        # it's important that the order of these arguments does not change\n        # if not also changed in the compiler's `function_scoping` method.\n        # the order is caller, keyword arguments, positional arguments!\n        if self.caller and not found_caller:\n            caller = kwargs.pop('caller', None)\n            if caller is None:\n                caller = self._environment.undefined('No caller defined',\n                                                     name='caller')\n            arguments.append(caller)\n\n        if self.catch_kwargs:\n            arguments.append(kwargs)\n        elif kwargs:\n            if 'caller' in kwargs:\n                raise TypeError('macro %r was invoked with two values for '\n                                'the special caller argument.  This is '\n                                'most likely a bug.' % self.name)\n            raise TypeError('macro %r takes no keyword argument %r' %\n                            (self.name, next(iter(kwargs))))\n        if self.catch_varargs:\n            arguments.append(args[self._argument_count:])\n        elif len(args) > self._argument_count:\n            raise TypeError('macro %r takes not more than %d argument(s)' %\n                            (self.name, len(self.arguments)))\n\n        return self._invoke(arguments, autoescape)\n\n    def _invoke(self, arguments, autoescape):\n        \"\"\"This method is being swapped out by the async implementation.\"\"\"\n        rv = self._func(*arguments)\n        if autoescape:\n            rv = Markup(rv)\n        return rv","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/inspector_protocol/jinja2/runtime.py#L546-L582","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Stop forwarding 'caller' as a keyword argument: drop caller=... from the inner macro invocation.","If the macro legitimately accepts catch_kwargs, filter out 'caller' before forwarding: pass **{k:v for k,v in kwargs.items() if k!='caller'}.","Restructure so only the {% call %} block supplies caller; inner macros receive their own call blocks.","Audit macro signatures and call sites for accidental double-supply of caller."],"exampleFix":"// before\n{% macro wrap(fn) %}{{ fn(caller=caller) }}{% endmacro %}\n// after\n{% macro wrap(fn) %}{{ fn() }}{% endmacro %}\n{# let the inner {% call %} block define its own caller #}","handlingStrategy":"validation","validationCode":"# Static scan: a {% call %} block whose body forwards caller=... into a macro\n# is the common trigger. Flag forwarding of caller kwarg.\nimport re, pathlib\nfor p in pathlib.Path('templates').rglob('*.html'):\n    src = p.read_text()\n    if re.search(r'\\{%\\s*call[^%]*%\\}.*caller\\s*=', src, re.S):\n        print(f'{p}: possible double-supply of caller')","typeGuard":"def forwards_caller_kwarg(macro_call_src: str) -> bool:\n    return 'caller=' in macro_call_src","tryCatchPattern":"# runtime TypeError is a bug; not recoverable. Fix the template.","preventionTips":["Never pass caller= explicitly; let {% call %} supply it.","When forwarding kwargs through a catch_kwargs macro, drop 'caller' first.","Add a template test that renders call blocks against sample data."],"tags":["jinja2","runtime","macro","caller","call-block"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}