RecursionError: maximum recursion depth exceeded means a function kept calling itself, directly or through other functions, until Python hit its safety limit of 1,000 nested calls. Usually the recursion never stops because the base case is missing or can be skipped. Sometimes it is hidden in a @property that sets itself, a __getattr__ or a __repr__. Fix the stopping condition first; only raise the limit with sys.setrecursionlimit() when the recursion is correct but genuinely deep, and prefer rewriting it as a loop.
The traceback usually tells you exactly which line repeats, thanks to the [Previous line repeated 996 more times] summary. Learning to read that line is half the fix.
All output below comes from Python 3.13.5 on my machine, with the main example also run on Python 3.10, 3.11 and 3.14.8 to show how the message changed.
In this guide:
- How to read the traceback
- Cause 1: No base case
- Cause 2: A base case that can be skipped
- Cause 3: A property that sets or reads itself
- Cause 4: __getattr__ that uses a missing attribute
- Cause 5: __repr__ on objects that point to each other
- Cause 6: Mutual recursion
- Cause 7: Correct recursion that is simply too deep
- Raising the limit with sys.setrecursionlimit()
- Cause 8: Deeply nested data, such as JSON
- The message changed in Python 3.11
- Quick reference
- Frequently asked questions
How to read the traceback
Here is the simplest case, a countdown with no stopping point:
def countdown(n):
print(n) if n % 250 == 0 else None
countdown(n - 1)
countdown(1000)
1000
750
500
250
Traceback (most recent call last):
File "c01_no_base.py", line 5, in <module>
countdown(1000)
~~~~~~~~~^^^^^^
File "c01_no_base.py", line 3, in countdown
countdown(n - 1)
~~~~~~~~~^^^^^^^
File "c01_no_base.py", line 3, in countdown
countdown(n - 1)
~~~~~~~~~^^^^^^^
File "c01_no_base.py", line 3, in countdown
countdown(n - 1)
~~~~~~~~~^^^^^^^
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
The output reached 250 and kept going below zero. The traceback shows three copies of line 3, then [Previous line repeated 996 more times], so the call on line 3 is the one that never stops. The RecursionError entry in the exceptions documentation describes it as raised when the interpreter detects that the maximum recursion depth is exceeded.
Cause 1: No base case
The countdown above has no condition that ends it. Every recursive function needs at least one path that returns without calling itself again:
def countdown(n):
if n < 0:
return
if n % 250 == 0:
print(n)
countdown(n - 1)
countdown(500)
print("finished")
500
250
0
finished
That first if is the base case. Put it at the top of the function, before the recursive call, so it is checked on every call. Note that I start at 500 here: with the base case added, countdown(1000) still failed in my test, because 1,001 nested calls plus the module itself are over the 1,000 limit. That is cause 7 below.
Cause 2: A base case that can be skipped
Having a base case is not enough if some inputs step right over it:
def halve_to_zero(n):
if n == 0:
return "done"
return halve_to_zero(n - 2)
print(halve_to_zero(10))
print(halve_to_zero(7))
done
Traceback (most recent call last):
File "c02_base_skipped.py", line 7, in <module>
print(halve_to_zero(7))
~~~~~~~~~~~~~^^^
File "c02_base_skipped.py", line 4, in halve_to_zero
return halve_to_zero(n - 2)
File "c02_base_skipped.py", line 4, in halve_to_zero
return halve_to_zero(n - 2)
File "c02_base_skipped.py", line 4, in halve_to_zero
return halve_to_zero(n - 2)
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
10 reached zero, but 7 went 5, 3, 1, -1 and never hit exactly zero. Using == in a base case is a common trap with steps bigger than 1, negative numbers and floats. Use <= instead:
def halve_to_zero(n):
if n <= 0:
return "done"
return halve_to_zero(n - 2)
print(halve_to_zero(10), halve_to_zero(7), halve_to_zero(-3))
done done done
Cause 3: A property that sets or reads itself
This one does not look recursive at all:
class User:
def __init__(self, name):
self.name = name
@property
def name(self):
return self.name
@name.setter
def name(self, value):
self.name = value.strip()
User(" Ana ")
Traceback (most recent call last):
File "c03_property.py", line 13, in <module>
User(" Ana ")
~~~~^^^^^^^^^
File "c03_property.py", line 3, in __init__
self.name = name
^^^^^^^^^
File "c03_property.py", line 11, in name
self.name = value.strip()
^^^^^^^^^
File "c03_property.py", line 11, in name
self.name = value.strip()
^^^^^^^^^
File "c03_property.py", line 11, in name
self.name = value.strip()
^^^^^^^^^
[Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded
Inside the setter, self.name = ... calls the setter again, forever. The getter has the same bug with return self.name. Store the real value under a different name, by convention with a leading underscore:
class User:
def __init__(self, name):
self.name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value.strip()
print(repr(User(" Ana ").name))
'Ana'
The property() documentation uses exactly this _x pattern in its example.
Cause 4: __getattr__ that uses a missing attribute
__getattr__ runs whenever normal attribute lookup fails. If it reads an attribute that does not exist yet, it calls itself:
class Config:
def __init__(self, data):
self.data = data
def __getattr__(self, key):
return self.data[key]
import copy
c = copy.copy(Config({"debug": True}))
Traceback (most recent call last):
File "c04_getattr.py", line 9, in <module>
c = copy.copy(Config({"debug": True}))
File "copy.py", line 98, in copy
return _reconstruct(x, None, *rv)
File "copy.py", line 261, in _reconstruct
if hasattr(y, '__setstate__'):
~~~~~~~^^^^^^^^^^^^^^^^^^^
File "c04_getattr.py", line 6, in __getattr__
return self.data[key]
^^^^^^^^^
File "c04_getattr.py", line 6, in __getattr__
return self.data[key]
^^^^^^^^^
File "c04_getattr.py", line 6, in __getattr__
return self.data[key]
^^^^^^^^^
[Previous line repeated 994 more times]
RecursionError: maximum recursion depth exceeded
copy.copy() creates the new object without calling __init__, so self.data does not exist. Reading it triggers __getattr__, which reads self.data again. Pickling hits the same problem. Raise AttributeError for internal names and for missing keys, which is also what hasattr() expects:
import copy
class Config:
def __init__(self, data):
self.data = data
def __getattr__(self, key):
if key == "data":
raise AttributeError(key)
try:
return self.data[key]
except KeyError:
raise AttributeError(key) from None
c = copy.copy(Config({"debug": True}))
print(c.debug)
True
The copy module documentation explains how classes can control copying with __copy__ and __deepcopy__. For a related "missing key" error in plain dicts, see my KeyError guide.
Cause 5: __repr__ on objects that point to each other
Two objects that reference each other will print each other forever if __repr__ includes the full object:
class Person:
def __init__(self, name):
self.name = name
self.partner = None
def __repr__(self):
return f"Person({self.name}, partner={self.partner})"
ana, ben = Person("Ana"), Person("Ben")
ana.partner, ben.partner = ben, ana
print(ana)
Traceback (most recent call last):
File "c07_repr.py", line 11, in <module>
print(ana)
~~~~~^^^^^
File "c07_repr.py", line 7, in __repr__
return f"Person({self.name}, partner={self.partner})"
^^^^^^^^^^^^^^
File "c07_repr.py", line 7, in __repr__
return f"Person({self.name}, partner={self.partner})"
^^^^^^^^^^^^^^
File "c07_repr.py", line 7, in __repr__
return f"Person({self.name}, partner={self.partner})"
^^^^^^^^^^^^^^
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
Printing ana prints ben, which prints ana, and so on. This often appears in a debugger or a log line, far from where the objects were created. Show a name or id for related objects instead:
class Person:
def __init__(self, name):
self.name = name
self.partner = None
def __repr__(self):
partner = self.partner.name if self.partner else None
return f"Person({self.name!r}, partner={partner!r})"
ana, ben = Person("Ana"), Person("Ben")
ana.partner, ben.partner = ben, ana
print(ana, ben)
Person('Ana', partner='Ben') Person('Ben', partner='Ana')
Built-in lists and dicts protect themselves from this by printing [...] for a cycle, but your own __repr__ does not get that for free. The reprlib module has a recursive_repr decorator that adds the same protection.
Cause 6: Mutual recursion
Two functions that call each other recurse just as deeply:
def is_even(n):
return True if n == 0 else is_odd(n - 1)
def is_odd(n):
return False if n == 0 else is_even(n - 1)
print(is_even(10))
print(is_even(-1))
The traceback starts like this:
True
Traceback (most recent call last):
File "c08_mutual.py", line 8, in <module>
print(is_even(-1))
~~~~~~~^^^^
File "c08_mutual.py", line 2, in is_even
return True if n == 0 else is_odd(n - 1)
~~~~~~^^^^^^^
File "c08_mutual.py", line 5, in is_odd
return False if n == 0 else is_even(n - 1)
~~~~~~~^^^^^^^
File "c08_mutual.py", line 2, in is_even
return True if n == 0 else is_odd(n - 1)
~~~~~~^^^^^^^
and ends like this, 3,003 lines later:
File "c08_mutual.py", line 5, in is_odd
return False if n == 0 else is_even(n - 1)
~~~~~~~^^^^^^^
File "c08_mutual.py", line 2, in is_even
return True if n == 0 else is_odd(n - 1)
~~~~~~^^^^^^^
RecursionError: maximum recursion depth exceeded
Notice there is no [Previous line repeated] summary, because the lines alternate between two functions, so the traceback is enormous. If your terminal fills with two alternating lines, that is mutual recursion. Here -1 moves away from zero on every call. Fixing the input fixes both functions:
def is_even(n):
n = abs(n)
return True if n == 0 else is_odd(n - 1)
def is_odd(n):
n = abs(n)
return False if n == 0 else is_even(n - 1)
print(is_even(10), is_even(-1), is_even(-4))
print(-4 % 2 == 0)
True False True
True
For this particular job, n % 2 == 0 is the real answer, as the last line shows.
Cause 7: Correct recursion that is simply too deep
Sometimes the logic is fine, but the input is large:
import sys
def total(items):
if not items:
return 0
return items[0] + total(items[1:])
print("limit:", sys.getrecursionlimit())
print(total(list(range(500))))
print(total(list(range(5000))))
limit: 1000
124750
Traceback (most recent call last):
File "c05_deep_valid.py", line 9, in <module>
print(total(list(range(5000))))
~~~~~^^^^^^^^^^^^^^^^^^^
File "c05_deep_valid.py", line 5, in total
return items[0] + total(items[1:])
~~~~~^^^^^^^^^^^
File "c05_deep_valid.py", line 5, in total
return items[0] + total(items[1:])
~~~~~^^^^^^^^^^^
File "c05_deep_valid.py", line 5, in total
return items[0] + total(items[1:])
~~~~~^^^^^^^^^^^
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
500 items worked; 5,000 needed 5,000 nested calls, over the default limit of 1,000. Python does not optimise tail calls, so rewriting it in tail-recursive style would not help either: every call still uses a stack frame. The best fix is a loop, and for tree-shaped data an explicit stack:
def total(items):
result = 0
for x in items:
result += x
return result
print(total(list(range(5000))), sum(range(5000)))
def depth(tree):
"""Max depth of nested lists without recursion, using an explicit stack."""
best, stack = 0, [(tree, 1)]
while stack:
node, d = stack.pop()
best = max(best, d)
stack.extend((child, d + 1) for child in node if isinstance(child, list))
return best
nested = []
cur = nested
for _ in range(50_000):
cur.append([])
cur = cur[0]
print(depth(nested))
12497500 12497500
50001
The explicit stack measured a tree nested 50,000 levels deep, far beyond what recursion could handle. The same file also shows the loop version of total() matching sum().
Raising the limit with sys.setrecursionlimit()
If you really need deeper recursion, you can raise the limit:
import sys
def total(items, i=0):
if i == len(items):
return 0
return items[i] + total(items, i + 1)
sys.setrecursionlimit(10_000)
print("limit:", sys.getrecursionlimit())
print(total(list(range(5000))))
limit: 10000
12497500
That worked for 5,000 levels. The sys.setrecursionlimit() documentation warns that the limit exists to stop recursion from overflowing the C stack and crashing Python, and a too-high limit can crash the interpreter outright instead of raising a clean error. Raise it only as far as you need, and never use it to hide a missing base case. For very deep work in a background thread, threading.stack_size() can give that thread a bigger stack.
Cause 8: Deeply nested data, such as JSON
You can hit the limit without writing any recursive code, because some parsers are recursive inside:
import json
text = "[" * 100_000 + "]" * 100_000
try:
json.loads(text)
except RecursionError as e:
print("RecursionError:", e)
RecursionError: maximum recursion depth exceeded while decoding a JSON array from a unicode string
A 100,000-level nested array made json.loads() fail with a more specific message. This matters for any service that parses JSON from users, because a small malicious payload can trigger it. Catch it and turn it into a normal validation error:
import json
def safe_loads(text):
try:
return json.loads(text)
except RecursionError:
raise ValueError("JSON nested too deeply") from None
try:
safe_loads("[" * 100_000 + "]" * 100_000)
except ValueError as e:
print("ValueError:", e)
print(safe_loads("[[1, 2], [3]]"))
ValueError: JSON nested too deeply
[[1, 2], [3]]
The json module documentation mentions RecursionError only for circular references when encoding, so guard deep input yourself. For writing JSON files safely, see my post on writing JSON to a file with pretty print.
The message changed in Python 3.11
On Python 3.10, the same countdown script ended with a different message, because the limit happened to be hit inside the n % 250 == 0 comparison on line 2:
File "c01_no_base.py", line 2, in countdown
print(n) if n % 250 == 0 else None
RecursionError: maximum recursion depth exceeded in comparison
Python 3.11 and 3.14.8 gave the plain message and pointed at the recursive call instead:
countdown(n - 1)
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
~~~~~~~~~^^^^^^^
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
So if you see maximum recursion depth exceeded in comparison or while calling a Python object in older logs, it is the same error; the suffix only says what Python was doing at the moment it ran out of depth.
Quick reference
| Pattern in the traceback | Cause | Fix |
|---|
| Same function line repeated, no if/return before it | No base case | Add a condition that returns without recursing |
| Works for some inputs, fails for others | Base case can be skipped (odd, negative, float) | Use <= or validate input |
Line inside a @property setter or getter | Property assigns or reads itself | Store the value in self._name |
Line inside __getattr__ | Lookup of a missing attribute calls __getattr__ again | Raise AttributeError for internal names |
Line inside __repr__ or __str__ | Two objects print each other | Print an id or name, not the whole object |
| Two functions alternate for thousands of lines | Mutual recursion without a reachable end | Fix the shared base case |
| Correct code, large input | Recursion deeper than 1,000 levels | Rewrite as a loop or explicit stack |
while decoding a JSON array | Deeply nested input data | Catch it and reject the input |
If your function returns the wrong value instead of crashing, a variable scoping bug may be the reason; my guide to UnboundLocalError covers the most common one.
Frequently asked questions
What does RecursionError: maximum recursion depth exceeded mean?
A function called itself, directly or through other functions, more times than Python allows, which is 1,000 levels by default. Usually the recursion never stops because the base case is missing or skipped.
What is the default recursion limit in Python?
1,000 in CPython. You can check it with sys.getrecursionlimit(). My Python 3.13.5 install reported 1000.
How do I increase the recursion limit?
Call sys.setrecursionlimit(n), for example sys.setrecursionlimit(10_000). Only do this when the recursion is correct but deep, and raise it no further than needed, because a too-high limit can crash Python.
How do I fix RecursionError in a property setter?
Store the value under another name, such as self._name, inside the setter and getter. Writing self.name = value inside the name setter calls the setter again forever.
Does Python have tail call optimisation?
No. Each recursive call uses a new stack frame even in tail position, so deep tail recursion still hits the limit. Rewrite it as a loop.
Why does my traceback not show Previous line repeated?
That summary only appears when the same line repeats. With mutual recursion, two functions alternate, so Python prints thousands of lines instead.
Can JSON input cause a RecursionError?
Yes. json.loads raised RecursionError on an array nested 100,000 levels deep in my test. Catch RecursionError around parsing untrusted input and treat it as invalid data.
Last checked: 9 November 2026. Figures, settings and guidance can change, so confirm anything important with the sources linked above.