How to increase the recursion limit in Python

Learn to increase Python's recursion limit. This guide covers methods, tips, real-world applications, and debugging common errors.

How to increase the recursion limit in Python
Published on: 
Tue
Mar 3, 2026
Updated on: 
Thu
Mar 5, 2026
The Replit Team Logo Image
The Replit Team

Python's recursion limit is a safeguard against stack overflows from deep function calls. For certain complex algorithms, you may need to adjust this default setting to execute your code.

In this article, you’ll learn techniques to safely increase the limit using sys.setrecursionlimit(). You'll also explore real-world applications, tips, and debugging advice to manage recursion effectively.

Using sys.setrecursionlimit() for basic limit adjustment

import sys
print(f"Default recursion limit: {sys.getrecursionlimit()}")
sys.setrecursionlimit(3000)
print(f"New recursion limit: {sys.getrecursionlimit()}")--OUTPUT--Default recursion limit: 1000
New recursion limit: 3000

The sys module gives you direct access to the Python interpreter's settings. The example code uses two of its functions to manage the recursion depth:

  • sys.getrecursionlimit() fetches the current limit, which is often 1000 by default.
  • sys.setrecursionlimit() sets a new, higher limit to prevent a RecursionError.

Increasing the limit tells the interpreter to reserve more memory for the call stack. This accommodates the longer chain of function calls required by algorithms that legitimately need deeper recursion, like processing a deeply nested tree structure.

Basic approaches to recursion limit management

While sys.setrecursionlimit() offers a quick fix, more advanced strategies provide safer and more flexible control over your application's recursion depth.

Checking the current recursion limit and testing depth

import sys

def recursive_function(n):
   if n <= 0:
       return
   recursive_function(n-1)
   
print(f"Current recursion limit: {sys.getrecursionlimit()}")
try:
   recursive_function(1100)  # Will exceed default limit
except RecursionError as e:
   print(f"Error: {e}")--OUTPUT--Current recursion limit: 1000
Error: maximum recursion depth exceeded while calling a Python object

This example demonstrates how to proactively test your code against the current recursion limit. By wrapping the function call in a try...except block, you can anticipate and manage a RecursionError without crashing your application.

  • The recursive_function is called with 1100, a value intentionally chosen to exceed the default limit of 1000.
  • Instead of crashing, the program catches the error and prints a message, showing a controlled failure.

This approach helps you confirm if your algorithm genuinely needs a higher limit or if there's a bug causing infinite recursion.

Creating a context manager for temporary limit changes

import sys
from contextlib import contextmanager

@contextmanager
def temporary_recursion_limit(new_limit):
   old_limit = sys.getrecursionlimit()
   sys.setrecursionlimit(new_limit)
   try:
       yield
   finally:
       sys.setrecursionlimit(old_limit)

with temporary_recursion_limit(2000):
   print(f"Inside context: {sys.getrecursionlimit()}")
print(f"Outside context: {sys.getrecursionlimit()}")--OUTPUT--Inside context: 2000
Outside context: 1000

A context manager offers a more robust way to handle recursion limits. It confines the change to a specific block of code using a with statement. This approach is safer because it guarantees the original limit is restored afterward—even if an error happens inside the block—preventing unintended side effects elsewhere in your application.

  • The @contextmanager decorator from the contextlib module turns a generator function into a reusable context manager.
  • A try...finally block is crucial. It sets the new limit, yields control to the with block, and then always restores the old limit in the finally clause.

Using threading stack size to support deeper recursion

import sys
import threading

print(f"Default thread stack size: {threading.stack_size()}")
# Increase thread stack size (in bytes)
threading.stack_size(2**27)  # 128MB stack
print(f"New thread stack size: {threading.stack_size()}")
# Now threads can handle deeper recursion--OUTPUT--Default thread stack size: 0
New thread stack size: 134217728

For multithreaded applications, you can increase the stack size for new threads to support deeper recursion. This method allocates more physical memory for a thread's call stack, which is a more robust solution than simply raising the interpreter's limit with sys.setrecursionlimit().

  • The threading.stack_size() function lets you set the stack size in bytes for any threads created afterward.
  • A larger stack provides the necessary memory to prevent a stack overflow crash when a thread runs a deeply recursive function.
  • A default value of 0 means the system's default stack size is used.

Advanced recursion management techniques

Moving beyond simple limit adjustments, advanced strategies involve redesigning your recursive logic itself for more robust and efficient execution.

Converting recursion to iteration with a stack

def factorial_recursive(n):
   return 1 if n <= 1 else n * factorial_recursive(n-1)

def factorial_iterative(n):
   result = 1
   stack = list(range(2, n+1))
   while stack:
       result *= stack.pop()
   return result

print(f"Recursive: {factorial_recursive(5)}")
print(f"Iterative: {factorial_iterative(5)}")--OUTPUT--Recursive: 120
Iterative: 120

You can bypass Python's recursion limit by converting a recursive function into an iterative one. This approach replaces the interpreter's call stack with a manual stack that you manage yourself, often using a simple list. This completely avoids the risk of a RecursionError.

  • The factorial_iterative function demonstrates this by building a stack of numbers to process.
  • A while loop then repeatedly takes a number from the stack using pop() and multiplies it, achieving the same result without deep function calls.

Creating a decorator for function-specific limit adjustment

import sys
from functools import wraps

def with_increased_recursion(limit=3000):
   def decorator(func):
       @wraps(func)
       def wrapper(*args, **kwargs):
           old_limit = sys.getrecursionlimit()
           sys.setrecursionlimit(limit)
           try:
               return func(*args, **kwargs)
           finally:
               sys.setrecursionlimit(old_limit)
       return wrapper
   return decorator

@with_increased_recursion(limit=1500)
def deep_recursion(n):
   return n if n <= 0 else deep_recursion(n-1) + 1--OUTPUT--# No output shown - the decorator allows the function to use a higher limit when called

A decorator offers a clean, reusable way to apply a temporary recursion limit to a specific function. It’s more targeted than a context manager because you apply it directly to the function definition, making the change self-contained and clear.

  • The @with_increased_recursion decorator takes a custom limit.
  • It uses a try...finally block to safely set the new limit before your function runs and restore the old one afterward.
  • Using @wraps from the functools module preserves the original function's metadata, which helps with debugging.

This approach neatly packages the logic, keeping your function's code clean.

Implementing tail-call optimization for recursive functions

import functools

def tail_call_optimized(func):
   @functools.wraps(func)
   def wrapper(*args, **kwargs):
       f = sys._getframe()
       if f.f_back and f.f_back.f_back and f.f_back.f_code == f.f_code:
           raise TailRecursionException(args, kwargs)
       result = func(*args, **kwargs)
       while True:
           try:
               return result
           except TailRecursionException as e:
               result = func(*e.args, **e.kwargs)
   return wrapper--OUTPUT--# No output shown - the decorator transforms recursive calls into iteration

Python doesn't natively support tail-call optimization, but this decorator cleverly simulates it. It transforms deep recursion into an iterative loop, effectively bypassing Python's recursion limit. This is a powerful way to handle algorithms that would otherwise cause a stack overflow.

  • The decorator uses sys._getframe() to inspect the call stack and detect when a function calls itself.
  • When a tail call occurs, it raises a custom TailRecursionException instead of making a new function call.
  • An outer while loop catches this exception and restarts the function with the new arguments, turning recursion into iteration.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. You can describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

For the advanced recursion techniques we've covered, Replit Agent can turn them into production-ready tools. It can take a concept that requires deep recursion and build a functional application around it.

  • Build a file system analyzer that recursively scans directories to map out a dependency tree, handling deeply nested project structures without crashing.
  • Create a syntax tree visualizer that parses code and displays its nested structure, using an iterative approach to handle large source files.
  • Deploy a graph traversal tool for social network analysis, using a custom decorator to safely manage recursion depth while finding connections in large datasets.

Describe your application idea, and Replit Agent will write the code, test it, and handle deployment, turning your concept into a working tool right in your browser.

Common errors and challenges

Managing Python's recursion limit introduces subtle challenges, from unintended side effects to sudden application crashes that are hard to debug.

  • Forgetting to reset the limit. A change made with sys.setrecursionlimit() is global. If you don’t restore the original value, it can cause unexpected behavior in other parts of your application or in third-party libraries that depend on the default setting. This is why using a context manager or decorator is the safest approach, as they automatically handle cleanup for you.
  • Avoiding system crashes. Setting the limit too high doesn't give your program infinite memory—it just pushes the boundary. If your recursion goes too deep, you won't get a neat Python RecursionError. Instead, you'll likely exhaust the C stack, causing a segmentation fault that crashes the entire interpreter without a chance to recover.
  • Detecting infinite recursion. A RecursionError is often a helpful sign of a bug, not just a low limit. If your function never reaches its base case, it will recurse forever. To debug this, you can add a depth-tracking parameter to your function and print its value on each call to see if it's growing uncontrollably.

Forgetting to reset the sys.setrecursionlimit() after processing

When you change the recursion limit with sys.setrecursionlimit(), the change is global. If you don't reset it, the new limit persists throughout your application, potentially causing issues in unrelated code. The following example shows how this oversight can happen.

import sys

def process_deep_data():
   # Increase limit for processing
   sys.setrecursionlimit(5000)
   process_nested_structure()
   # Never resets the limit!

Because process_deep_data never resets the recursion limit, the elevated setting remains active globally after the function finishes. The following code demonstrates how this oversight can cause problems in an unrelated part of the application.

import sys

def process_deep_data():
   old_limit = sys.getrecursionlimit()
   try:
       sys.setrecursionlimit(5000)
       process_nested_structure()
   finally:
       sys.setrecursionlimit(old_limit)

The key is the try...finally block. It guarantees the original limit is restored because the finally clause always runs, even if an error occurs. This prevents the change from affecting other parts of your application. You should use this pattern whenever you modify global settings like the recursion limit, especially in library code or large projects where functions might be used in unexpected ways. This approach ensures your code is robust and self-contained.

Avoiding system crashes when setting sys.setrecursionlimit() too high

Raising the recursion limit with sys.setrecursionlimit() seems like an easy fix, but setting it too high is dangerous. It bypasses Python's safety net, risking a hard system crash instead of a manageable RecursionError. The code below demonstrates this exact scenario.

import sys

# Dangerously high recursion limit
sys.setrecursionlimit(1000000)

def factorial(n):
   return 1 if n <= 1 else n * factorial(n-1)

result = factorial(50000)  # Will likely crash

The factorial(50000) call exhausts the C stack before Python's limit check can trigger a RecursionError. This results in a hard crash instead of a manageable exception. The next example shows how to prevent this.

import sys

# Reasonable increase
sys.setrecursionlimit(3000)

def factorial(n):
   result = 1
   for i in range(2, n+1):
       result *= i
   return result

result = factorial(50000)  # Safe iterative approach

The safest solution is to rewrite the recursive logic as an iterative one. The factorial function now uses a for loop instead of calling itself, which completely avoids deep recursion. This allows it to handle large inputs without risking a stack overflow or needing a dangerously high limit. You should always favor this approach for algorithms that require many steps, as it sidesteps the recursion limit entirely and prevents system crashes.

Detecting and debugging infinite recursion with depth tracking

A RecursionError often signals a bug, not just a low limit. Infinite recursion happens when a function never hits its base case and calls itself endlessly. Depth tracking helps you spot this by monitoring how deep the calls go.

The code below demonstrates how easily this can happen when a data structure contains a cycle, causing the traverse_tree function to loop forever.

def traverse_tree(node):
   print(f"Node: {node['id']}")
   for child in node.get('children', []):
       traverse_tree(child)

node = {'id': 1, 'children': []}
node['children'].append(node)  # Creates a cycle
traverse_tree(node)  # Infinite recursion!

The traverse_tree function doesn't track which nodes it has already visited. When it encounters the cycle where a node is its own child, it gets trapped in an infinite loop. The corrected implementation below shows how to fix this.

def traverse_tree(node, visited=None):
   if visited is None:
       visited = set()
   if id(node) in visited:
       return
   visited.add(id(node))
   print(f"Node: {node['id']}")
   for child in node.get('children', []):
       traverse_tree(child, visited)

To fix the infinite loop, the corrected traverse_tree function uses a visited set to remember every node it processes. Before traversing a node, it checks if its unique ID is already in the visited set. If so, it returns immediately, breaking the cycle. This technique is essential when working with data structures like graphs or object trees where circular references can occur, ensuring your function always terminates.

Real-world applications

With these management techniques, you can tackle real-world challenges like parsing deeply nested data or building complex object trees.

Parsing deeply nested JSON data structures with sys.setrecursionlimit()

A recursive function is often the most intuitive way to process deeply nested JSON, and temporarily increasing the limit with sys.setrecursionlimit() allows you to handle complex structures without triggering a RecursionError.

import sys
import json

sys.setrecursionlimit(3000)  # Increase limit for deeply nested JSON

def parse_nested_json(data, depth=0):
   if isinstance(data, dict):
       return {k: parse_nested_json(v, depth+1) for k, v in data.items()}
   elif isinstance(data, list):
       return [parse_nested_json(item, depth+1) for item in data]
   return data

nested_json = {"a": {"b": {"c": {"d": {"e": "value"}}}}}
result = parse_nested_json(nested_json)
print(f"Successfully parsed JSON with depth {len(str(nested_json))} characters")

This example shows how to process data with many nested layers. The parse_nested_json function works by calling itself to dig deeper into the structure.

  • If it finds a dictionary, it recursively processes each value.
  • If it encounters a list, it does the same for each item.

The function stops when it hits a simple value, like a string. Because deeply nested data can exceed Python's standard call stack capacity, sys.setrecursionlimit(3000) is set beforehand. This ensures the program has enough stack space to traverse the entire structure.

Building large recursive data structures with adjusted limits

Your recursive function can also dynamically call sys.setrecursionlimit() as it runs, letting you build large data structures without needing to set a high, arbitrary limit upfront.

import sys

def create_nested_dict(depth):
   if depth <= 0:
       return "leaf"
   
   if depth > sys.getrecursionlimit() - 50:
       new_limit = depth + 100
       sys.setrecursionlimit(new_limit)
       print(f"Adjusted recursion limit to {new_limit}")
       
   return {"nested": create_nested_dict(depth - 1)}

deep_dict = create_nested_dict(900)
print(f"Created a dictionary nested to depth 900")

The create_nested_dict function constructs a dictionary by calling itself until the depth reaches zero. It includes a conditional check that compares the current depth to the value from sys.getrecursionlimit().

  • This check is designed to trigger when the function is 50 calls away from the limit.
  • When triggered, it executes sys.setrecursionlimit(), setting a new limit based on the current depth plus an additional 100 calls. This allows the recursion to proceed further.

Get started with Replit

Turn these techniques into a real tool. Describe what you want to build, like "a web app that parses deeply nested JSON and visualizes it" or "a file system analyzer that maps directory trees without hitting recursion limits."

Replit Agent writes the code, tests for errors, and deploys your app. Start building with Replit.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.