How to Resolve "RecursionError: Maximum Recursion Depth Exceeded" in Python
Quick answer
A recursive function that should eventually finish instead crashes after making far more calls than expected. Python enforces a default recursion depth limit...
A recursive function that should eventually finish instead crashes after making far more calls than expected. Python enforces a default recursion depth limit specifically to catch runaway recursion before it exhausts the actual call stack and crashes the interpreter less gracefully โ the fix is almost always in the recursive logic itself, not in the limit.
The Problem
A function that looked correct fails with a deep, repetitive traceback:
Traceback (most recent call last):
File "script.py", line 8, in factorial
return n * factorial(n - 1)
File "script.py", line 8, in factorial
return n * factorial(n - 1)
File "script.py", line 8, in factorial
return n * factorial(n - 1)
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
Sometimes the recursive call is much less obvious, hidden inside object serialization or a tree traversal:
RecursionError: maximum recursion depth exceeded while calling a Python object
Why It Happens
Python's default recursion limit (typically 1000 frames) exists to catch infinite or excessively deep recursion before it crashes the process with a raw stack overflow, which would be far messier and less debuggable than a clean Python exception. This error appears for a few distinct reasons:
- A missing or incorrect base case โ the recursive function never reaches a condition that stops calling itself, so it keeps recursing indefinitely (or until the limit is hit).
- Genuinely deep but valid recursion โ processing a deeply nested data structure (a long linked list, a deeply nested JSON document, a large unbalanced tree) that legitimately requires more stack frames than the default limit allows.
- Mutual recursion creating an unintended cycle โ function A calls function B, which calls function A again, without either side making progress toward a terminating condition.
- An object's
__repr__or serialization logic recursing into itself โ a common trap with objects that contain a reference back to themselves or to each other, discovered when something tries to print or serialize the object tree.
The Fix
First, check for a missing or unreachable base case โ this is the most common actual bug, not a legitimate need for deeper recursion:
# Bug: base case condition is never actually reached
def factorial(n):
return n * factorial(n - 1) # missing: if n == 0, return 1
# Fixed
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
For mutual recursion, trace through the logic manually or add temporary debug prints to confirm both functions are actually converging toward their stopping condition rather than looping indefinitely:
def is_even(n):
if n == 0:
return True
return is_odd(n - 1)
def is_odd(n):
if n == 0:
return False
return is_even(n - 1) # make sure n actually decreases each call
If the recursion is genuinely correct but needs to handle deeper structures than the default limit allows, converting to an iterative approach using an explicit stack is usually more robust than just raising the limit, since it isn't bounded by Python's C-level stack size at all:
# Iterative equivalent using an explicit stack instead of the call stack
def factorial_iterative(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
For tree or graph traversals specifically, an explicit stack-based approach avoids recursion entirely while achieving the same traversal order:
def traverse_iterative(root):
stack = [root]
while stack:
node = stack.pop()
process(node)
stack.extend(node.children)
If you've confirmed the recursion is correct and genuinely needs more depth than the default allows (rare, and worth being cautious about), raise the limit carefully โ this doesn't increase your actual C stack size, so setting it too high can still crash the interpreter with a segmentation fault instead of a clean Python exception:
import sys
sys.setrecursionlimit(3000)
Still Not Working?
If raising the limit just delays the same error at a slightly deeper point rather than fixing it, that's strong confirmation the recursion genuinely has no valid terminating condition rather than just needing more room โ go back to reviewing the base case logic rather than continuing to raise the limit further. For debugging exactly where the recursion is failing to terminate, print the function's arguments on each call temporarily to see the actual pattern of values it's working through:
def factorial(n, depth=0):
print(f"{' ' * depth}factorial({n})")
if n == 0:
return 1
return n * factorial(n - 1, depth + 1)
Watching the printed sequence of values makes it immediately obvious whether the recursion is converging toward the base case or stuck oscillating, growing, or repeating without making real progress.