Failed to render model: maximum recursion depth exceeded" dbt macro
Fixes dbt macro infinite recursion by finding the macro call cycle and adding a depth limit or rewriting the logic iteratively. Use when dbt fails with maximum recursion depth exceeded during rendering. Not for deep but legitimate nesting, which needs a higher limit rather than a rewrite.
TL;DR
A macro calls itself (directly or through a cycle of macros) without a stopping condition, and Python's recursion limit kills the render. Find the cycle in the traceback, add a depth counter with a maximum, or rewrite the logic as a loop. Then recompile.
Error
"Failed to render model: maximum recursion depth exceeded" dbt macroSteps
- Re-run with
--debugand read the traceback: the repeating macro frame names the cycle. Expected: you see the same macro (or macro pair) calling itself. - Open the macro and find the recursive call. Expected: you see the call with no base case, or a base case that never triggers.
- Add a depth parameter with a hard stop, for example
{% macro walk(node, depth=0) %}{% if depth > 50 %}{{ raise('too deep') }}{% endif %}.... Expected: runaway recursion now fails fast with a clear message instead of a depth error. - Better, rewrite the recursion as an explicit loop over a work list when the logic allows it. Expected: no recursive macro calls remain.
- Test with
dbt run-operation [MACRO NAME] --args '{...}', thendbt compile. Expected: the macro completes and the project compiles.
When to use
- dbt fails with "maximum recursion depth exceeded" during rendering.
- You just added recursion to a macro (tree walks, nested struct flattening).
When not to use
- The nesting is legitimately deep but finite (raise the limit instead of rewriting).
- The error happens in Python adapter code rather than your macro.
Tool compatibility
- dbt Core 1.0 and later, all adapters. Jinja recursion limits come from Python.
Variant phrasings
RecursionError in macro during dbt compile
The raw Python form of the same failure.
Macro hangs instead of erroring
A cycle that grows slowly can look like a hang; the depth guard turns it into a fast, clear failure.
Why it happens
Jinja macros can call other macros, including themselves. Without a base case, or with a base case that never matches the real data, the call stack grows until Python refuses.
Edge cases
- Mutual recursion (macro A calls B calls A) hides the cycle; the traceback shows the alternating frames.
- Data-driven recursion over deeply nested JSON can exceed the limit on legitimate data; raise the limit cautiously and add the guard anyway.
run-operationreproduces macro recursion without compiling the whole project, which is much faster to iterate on.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_MyqI4ULWsY3hpvOXGfJ3Dg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.