RecursionError: maximum recursion depth exceeded (tested)
RecursionError: maximum recursion depth exceeded means a function called itself (directly or indirectly) more times than Python's recursion limit allows, almost always because it's missing a base case that would stop the recursion, or the base case is unreachable for the input given.
1. The default limit and five ways to hit it
Python's default recursion limit on this interpreter (3.13.5) is 1000 stack frames, checked with sys.getrecursionlimit(). We triggered the same error five different ways; the message text is identical across all of them in this Python version:
| Code pattern | Exact error |
|---|---|
def f(): f() — no base case at all | RecursionError: maximum recursion depth exceeded |
def fact(n): return n * fact(n-1) — missing if n == 0: return 1 | RecursionError: maximum recursion depth exceeded |
Two functions calling each other (a() calls b() calls a()), neither has a base case | RecursionError: maximum recursion depth exceeded |
A class whose __eq__ compares self == other instead of a real field | RecursionError: maximum recursion depth exceeded |
A class whose __repr__ calls repr(self) instead of formatting fields | RecursionError: maximum recursion depth exceeded |
Older Python versions and many existing tutorials show longer messages with a suffix, like "...exceeded while calling a Python object" or "...exceeded in comparison". On this interpreter (3.13.5) every case above produced the same plain message with no suffix — if you're on an older Python and see a suffixed version, it's the same error, just older wording.
2. The real bug is almost always a missing or unreachable base case
A working recursive function needs a condition that stops it without calling itself again:
# broken: no base case, recurses forever
def fact(n):
return n * fact(n - 1)
# fixed: base case stops the recursion at n == 0
def fact(n):
if n == 0:
return 1
return n * fact(n - 1)
A base case that exists but is unreachable causes the same error — for example checking if n == 0 but calling with a negative number, which counts down past zero forever (-1, -2, -3, ...), or a float input that never exactly equals an integer base case.
3. Mutual recursion hides the problem across two functions
The same missing-base-case bug, split across two functions, is harder to spot because neither one looks recursive on its own:
def is_even(n):
if n == 0: return True
return is_odd(n - 1)
def is_odd(n):
if n == 0: return False # base case present here
return is_even(n - 1) # but called with n < 0, it never hits either base case
Fix: trace the actual call chain, not just one function, and check that every path through both functions terminates.
4. Dunder methods can recurse invisibly
A subtle variant: implementing __eq__, __repr__, or __str__ in a way that calls itself instead of a real attribute. This is easy to miss because the recursive call doesn't look like a function call — it's triggered by ==, print(), or f-strings:
class Bad:
def __eq__(self, other):
return self == other # calls __eq__ again, forever
>>> Bad() == Bad()
RecursionError: maximum recursion depth exceeded
Fix: compare specific attributes (self.value == other.value), never self against other directly inside __eq__ itself.
5. Deep-but-legitimate recursion: raise the limit, or don't
A genuinely large but finite recursion (deep tree traversal, unmemoized Fibonacci) can also exceed the default 1000-frame limit even with a correct base case. sys.setrecursionlimit(n) raises the ceiling, but it does not create more actual C stack space — set it too high and you risk a real segmentation fault instead of a catchable Python exception, which is strictly worse.
import sys
sys.setrecursionlimit(5000) # raises the ceiling, doesn't add real stack space
For deep-but-legitimate recursion, prefer converting to an iterative loop with an explicit stack/queue, or add memoization (functools.lru_cache) if the same calls repeat, which often reduces both the depth and the total work.
6. You can catch it, but reconsider doing so inside the recursive function itself
RecursionError is a regular exception, catchable with except RecursionError:. But catching it inside the same recursive function re-triggers the handler at every stack level as the exception unwinds, which we confirmed: a try/except RecursionError: raise wrapped around the recursive call printed its handler once per unwound frame, not once total. Catch it at the top-level caller instead, not inside the recursive function.
7. Fix checklist
- Every recursive function needs a base case that is actually reachable from every valid input, including edge cases like negative numbers or empty input.
- For mutual recursion, trace the call chain across both functions, not just one.
- Check
__eq__/__repr__/__str__for comparisons or formatting that referenceselfrecursively instead of a concrete field. - For deep-but-correct recursion, prefer an iterative rewrite or
functools.lru_cacheover raisingsys.setrecursionlimit(), which trades a safe Python exception for a riskier hard crash. - Catch
RecursionErrorat the outermost call, not inside the recursive function.
Frequently asked questions
What does RecursionError: maximum recursion depth exceeded mean?
A function called itself, directly or through other functions, more times than Python's recursion limit (1000 by default) allows without ever hitting a condition that stops the recursion. It is almost always caused by a missing or unreachable base case.
Why doesn't my function's base case stop the recursion?
The base case may be unreachable for the actual inputs reaching it - for example checking n == 0 while calling with negative numbers, which count past zero forever, or using == on floats that never land exactly on the base value.
Is it safe to just raise the recursion limit with sys.setrecursionlimit()?
Only a little, and not as a real fix. It raises Python's own counter but does not add actual C stack space, so setting it too high risks a hard segmentation fault instead of a catchable RecursionError. Prefer an iterative rewrite for genuinely deep recursion.
Can a class's __eq__ or __repr__ method cause this error?
Yes. If __eq__ compares self == other instead of specific attributes, or __repr__ calls repr(self) instead of formatting fields, each call triggers another call to the same method, recursing until the limit is hit. We reproduced both on Python 3.13.5.
Should I catch RecursionError inside the recursive function itself?
No - we tested this and the except block re-runs once per stack frame as the exception unwinds, not once total, producing repeated output. Catch it once at the outermost, non-recursive caller instead.
Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5 on Linux using plain python3 one-liners. Every error below is copy-pasted output, not paraphrased. PlainHeaders is our extension; Python is free.