pythonseverity: workaround
RecursionError

Python RecursionError: maximum recursion depth exceeded

Maximum recursion depth exceeded

85% fixable~15 mindifficulty: intermediate

Verified against CPython 3.13 source (Python/ceval.c), Python docs: sys.setrecursionlimit, Python docs: built-in-exceptions · Updated April 2026

> quick_fix

Your code recursed more than 1000 levels deep (the CPython default). Either the base case is missing or wrong, or the function genuinely needs deeper recursion. Most often, convert the recursion to a while-loop with an explicit stack. Raising sys.setrecursionlimit is a workaround, not a fix.

# Stack overflow if list is large
def sum_list(items):
    if not items:
        return 0
    return items[0] + sum_list(items[1:])

# Fix - iterate
def sum_list(items):
    total = 0
    for x in items:
        total += x
    return total

# Or use built-in
total = sum(items)

What causes this error

CPython enforces a stack-depth limit (default 1000) to prevent runaway recursion from segfaulting the interpreter via OS stack overflow. When Py_EnterRecursiveCall hits the limit, RecursionError is raised. Python doesn't have tail-call optimisation, so even tail-recursive code consumes a stack frame per call.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Find the recursive function in the trace

    The traceback shows the same function calling itself many times. Identify the function and check its base case.

  2. 02

    step 2

    Verify the base case fires

    Add a print at the top of the function showing the input. If the input never reaches the base case, the recursion will overflow regardless of limit.

  3. 03

    step 3

    Convert to iteration with an explicit stack

    For tree or graph traversal, replace recursion with a while-loop and a deque/list as the work stack. This is the standard idiomatic fix in Python.

    from collections import deque
    
    def walk(root):
        stack = deque([root])
        while stack:
            node = stack.pop()
            process(node)
            stack.extend(node.children)
  4. 04

    step 4

    For deep but bounded recursion, raise sys.setrecursionlimit

    If the function is correct but the input is genuinely deep (tree of depth 1500), set sys.setrecursionlimit(5000). Don't go above 10000 - the OS thread stack is also bounded and you'll get a segfault, not a clean Python error.

  5. 05

    step 5

    Use functools.lru_cache for memoisation

    If the recursion has overlapping subproblems (Fibonacci, dynamic programming), @lru_cache prevents repeated calls and often eliminates the depth issue.

How to verify the fix

  • RecursionError no longer fires.
  • Function returns correct results on the previously-failing input.
  • Memory usage is reasonable (iteration usually wins by a lot).

Why RecursionError happens at the runtime level

CPython tracks recursion depth via tstate->recursion_depth, incremented by Py_EnterRecursiveCall in Python/ceval.c at every Python-level function call (and at certain C-API entry points like PyObject_Call). The limit defaults to 1000 (settable via sys.setrecursionlimit) and exists because the OS thread stack is finite (typically 8MB on Linux, allowing roughly 10,000 frames before SIGSEGV). When recursion_depth crosses the limit, the interpreter raises RecursionError immediately, before the OS-level stack overflow that would crash the process. Python lacks TCO so every recursive call costs a real frame.

Common debug mistakes for RecursionError

  • Setting sys.setrecursionlimit(100000) in module top-level expecting to support deep recursion - the OS stack overflows around 10,000-12,000 frames and the process segfaults, often with no Python traceback.
  • Adding @lru_cache without an eq-comparable argument - the cache key is the args tuple, and unhashable args bypass the cache, leaving recursion just as deep.
  • Implementing __getattr__ that recursively accesses self.something - if 'something' is also undefined, __getattr__ calls itself forever and overflows on the first miss.
  • Serialising a deeply-nested object - many serialisation libraries use recursion internally and can hit the limit on lists of nested dicts even without your own recursion.
  • Using JSON encoder default() that calls itself for nested objects without a visited set - circular references cause infinite recursion.

When RecursionError signals a deeper problem

Recurring RecursionError on production data is a sign the algorithm choice doesn't match the input shape. Recursion is elegant for code with bounded depth (binary trees, AST walks), but production data often has deeper structure than test fixtures (log file trees, deeply nested JSON, graph cycles). The architectural fix is to default to iteration with explicit stacks for any traversal that touches user-provided data, reserving recursion for code where depth is provably small. Combined with input-validation that caps nesting depth at the API boundary, the error class disappears. Without this, every new feature risks tripping the limit on customer data.

Frequently asked questions

Why doesn't Python optimise tail calls?

Guido van Rossum has explicitly rejected TCO in CPython - he prefers explicit stack traces for debuggability. So even tail-recursive functions consume one stack frame per call.

How high can I safely set sys.setrecursionlimit?

Up to ~5000 on most platforms; beyond that, the OS thread stack overflows before the Python check fires. For deeper recursion, you must convert to iteration.

Does RecursionError affect threads or async tasks?

Each thread has its own stack, so each thread has its own recursion budget. asyncio tasks share the main thread's stack, so deep recursion inside async functions has the same limit as sync code.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is manually verified against official sources listed in the “sources” sidebar. If a fix here didn’t work for you, please email so we can update the page.