{"record":{"id":"753d589e06ebb6ae","repo":"nodejs/node","slug":"tried-to-call-non-recursive-loop-maybe-you-forgo","errorCode":null,"errorMessage":"Tried to call non recursive loop.  Maybe you forgot the 'recursive' modifier.","messagePattern":"Tried to call non recursive loop\\.  Maybe you forgot the 'recursive' modifier\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"tools/inspector_protocol/jinja2/runtime.py","lineNumber":404,"sourceCode":"    @property\n    def previtem(self):\n        if self._before is _first_iteration:\n            return self._undefined('there is no previous item')\n        return self._before\n\n    @property\n    def nextitem(self):\n        if self._after is _last_iteration:\n            return self._undefined('there is no next item')\n        return self._after\n\n    def __len__(self):\n        return self.length\n\n    @internalcode\n    def loop(self, iterable):\n        if self._recurse is None:\n            raise TypeError('Tried to call non recursive loop.  Maybe you '\n                            \"forgot the 'recursive' modifier.\")\n        return self._recurse(iterable, self._recurse, self.depth0 + 1)\n\n    # a nifty trick to enhance the error message if someone tried to call\n    # the the loop without or with too many arguments.\n    __call__ = loop\n    del loop\n\n    def __repr__(self):\n        return '<%s %r/%r>' % (\n            self.__class__.__name__,\n            self.index,\n            self.length\n        )\n\n\nclass LoopContext(LoopContextBase):\n","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/inspector_protocol/jinja2/runtime.py#L386-L422","documentation":"Raised by LoopContext.loop(iterable) when self._recurse is None, i.e. the LoopContext was not created in a recursive loop. The loop() method is the callable injected into templates to recurse; calling it makes sense only inside {% for x in seq recursive %...{% endfor %}. Calling loop(...) in a non-recursive for-loop is almost always a template bug.","triggerScenarios":"A template uses {{ loop(item.children) }} inside a normal {% for %} block that lacks the 'recursive' modifier. Copy-pasting a recursive macro body into a non-recursive loop. Renaming a variable that shadowed 'loop' so the inner call now hits the outer (non-recursive) LoopContext.","commonSituations":"Refactoring recursive tree rendering into a flat loop and forgetting to remove loop() calls. Helpers/macros originally written for recursive use and reused in flat contexts. Confusion between loop.index and loop() (calling instead of indexing).","solutions":["Add the 'recursive' modifier: {% for node in tree recursive %}...{{ loop(node.children) }}...{% endfor %}.","If recursion is not intended, replace loop(...) with a macro call or a separate {% include %}.","Verify the variable named 'loop' has not been shadowed by a local variable inside the loop body.","Move recursive rendering into a macro that takes the sequence, and call the macro instead of loop()."],"exampleFix":"// before\n{% for node in tree %}\n  {{ node.name }}\n  {{ loop(node.children) }}\n{% endfor %}\n// after\n{% for node in tree recursive %}\n  {{ node.name }}\n  {{ loop(node.children) }}\n{% endfor %}","handlingStrategy":"validation","validationCode":"# Static validation: scan templates for loop(...) calls and ensure the\n# enclosing {% for %} has the 'recursive' modifier.\nimport re, pathlib\nfor p in pathlib.Path('templates').rglob('*.html'):\n    src = p.read_text()\n    for m in re.finditer(r'\\{%\\s*for\\s+([^{]+?)\\s+in\\s+([^{]+?)( recursive)?\\s*%\\}(.*?)\\{%\\s*endfor\\s*%\\}', src, re.S):\n        if 'loop(' in m.group(4) and not m.group(3):\n            print(f'{p}: loop() used without recursive modifier')","typeGuard":"def loop_is_recursive(for_tag_text: str) -> bool:\n    return 'recursive' in for_tag_text","tryCatchPattern":"# not recoverable at runtime; fix the template\n# a syntax/lint check at build time is the right layer","preventionTips":["Run a template linter that flags loop() calls outside recursive {% for %} blocks.","Keep recursive tree rendering in dedicated macros to avoid copy-paste into flat loops.","Avoid shadowing the 'loop' variable inside loops."],"tags":["jinja2","runtime","loop","recursive","template"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}