How to fix RangeError: Maximum call stack size exceeded (infinite recursion) in Node.js
This error occurs when a function calls itself recursively without a proper base case or termination condition, exhausting the call stack. Stack overflow can also happen during deep object operations, circular event listeners, or when processing deeply nested data structures.
Quick fix
Fixes RANGE_ERROR
RangeError: Maximum call stack size exceeded
at calculateFactorial (script.js:5:3)
at calculateFactorial (script.js:6:3)
at calculateFactorial (script.js:6:3)
at calculateFactorial (script.js:6:3)
...repeated thousands of times...- Identify the function causing the stack overflow
- Add a proper base case to recursive functions
- Replace recursion with iteration when processing large datasets
- Fix event listener infinite loops
RangeError: Maximum call stack size exceededat calculateFactorial (script.js:5:3)at calculateFactorial (script.js:6:3)at calculateFactorial (script.js:6:3)at calculateFactorial (script.js:6:3)...repeated thousands of times...RangeError: Maximum call stack size exceeded (infinite recursion)
What this error means
A stack overflow happens when the call stack (a limited memory area that tracks function calls) becomes full. Every time a function is called, a new frame is added to the stack. In Node.js, there is a hard limit to how many frames can exist at once (typically around 10,000-30,000 depending on the system). When this limit is exceeded, the JavaScript engine throws a RangeError. This usually indicates infinite recursion where a function keeps calling itself without a way to stop, but can also result from deeply nested callback chains, circular event emissions, or operations on very large/deeply nested objects. The stack overflow indicates a bug in your code logic rather than a system resource shortage.
Typical symptoms
- Application crashes immediately with "RangeError: Maximum call stack size exceeded"
- Error appears to happen during a specific operation or function call
- Stack trace is very long and shows the same function repeating
- Performance suddenly drops before the crash (due to excessive function calls)
- Recursive function calls grow without visible progress
- Event listeners trigger themselves in an infinite loop
- Deep object cloning or serialization operations fail
Common causes
- Recursive function missing base case or termination condition
- Recursive function base case never being reached due to logic error
- Event emitter triggering its own listener, creating infinite recursion
- JSON.stringify() or similar operation on circular or very deeply nested objects
- Deeply nested callback chains that don't properly break
- Processing arrays or objects with extreme depth without iteration
- Promise chaining creating excessive stack depth without proper handling
- Mutual recursion between two or more functions without base cases
How to fix "RangeError: Maximum call stack size exceeded (infinite recursion)"
Look at the error stack trace carefully. Stack overflow errors typically show the same function name repeated many times:
RangeError: Maximum call stack size exceeded
at calculateFactorial (script.js:5:3)
at calculateFactorial (script.js:6:3)
at calculateFactorial (script.js:6:3)
at calculateFactorial (script.js:6:3)
...repeated thousands of times...The function name that repeats is your culprit. In this example, it's calculateFactorial. Run your script with increased logging to trace when the recursion starts:
let callCount = 0;
function calculateFactorial(n) {
callCount++;
console.log(`Call #${callCount}: n=${n}`);
if (callCount > 100) {
console.log('Stack overflow detected, exiting early');
process.exit(1);
}
return calculateFactorial(n - 1);
}Every recursive function MUST have a base case—a condition that stops the recursion. Without it, the function calls itself infinitely.
Before (infinite recursion):
function countdown(n) {
console.log(n);
return countdown(n - 1); // Always calls itself!
}
countdown(5); // RangeError: Maximum call stack size exceededAfter (with base case):
function countdown(n) {
if (n === 0) {
console.log('Done!');
return; // Base case: stop recursion
}
console.log(n);
return countdown(n - 1);
}
countdown(5); // Logs: 5, 4, 3, 2, 1, Done!The base case is the if (n === 0) condition. It's the "stopping point" that prevents infinite recursion.
Recursion consumes stack space for each call. For large datasets, iteration is safer and faster:
Recursive (risky for large arrays):
function sumArray(arr, index = 0) {
if (index === arr.length) {
return 0;
}
return arr[index] + sumArray(arr, index + 1);
}
const largeArray = new Array(50000).fill(1);
sumArray(largeArray); // RangeError: stack overflowIterative (safe for large arrays):
function sumArray(arr) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
const largeArray = new Array(50000).fill(1);
console.log(sumArray(largeArray)); // Works fine: 50000Loops don't add stack frames, so they handle arbitrarily large datasets without overflow.
If you're emitting an event inside its own listener, you create infinite recursion:
Before (infinite loop):
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
emitter.on('data', (value) => {
console.log(value);
emitter.emit('data', value - 1); // Emits itself!
});
emitter.emit('data', 5); // RangeErrorAfter (with base case):
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
emitter.on('data', (value) => {
console.log(value);
if (value > 0) {
emitter.emit('data', value - 1);
}
});
emitter.emit('data', 5); // Logs: 5, 4, 3, 2, 1, 0Add the termination condition BEFORE emitting the event again.
JSON.stringify() can fail on deeply nested or circular objects:
Risky code:
const obj = { value: 1 };
obj.self = obj; // Circular reference!
JSON.stringify(obj); // RangeError: Maximum call stack size exceededSolution 1: Use a replacer function:
const seen = new WeakSet();
const json = JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]'; // Skip circular refs
}
seen.add(value);
}
return value;
});Solution 2: Clone objects safely:
function deepClone(obj, depth = 100) {
if (depth === 0) {
console.warn('Max depth reached, truncating object');
return null;
}
if (typeof obj !== 'object' || obj === null) return obj;
const cloned = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key], depth - 1);
}
}
return cloned;
}Node.js allows you to increase the stack size temporarily, but this is NOT a fix—only a diagnostic tool:
node --stack-size=2000 script.jsThe --stack-size flag sets stack size in kilobytes. This might allow your script to run slightly longer, helping you see where the infinite recursion occurs:
// Add this at the top of your script
let callCount = 0;
const originalFunc = someFunction;
someFunction = function(...args) {
callCount++;
if (callCount % 1000 === 0) {
console.log(`Made ${callCount} calls so far...`);
}
return originalFunc(...args);
};Use this to identify where the recursion starts spinning, but always fix the underlying logic issue. Increasing stack size is a bandage, not a solution.
Advanced notes
Tail call optimization:
Some languages optimize "tail recursion" (where the recursive call is the last statement). Unfortunately, Node.js/V8 does NOT perform tail call optimization, so even tail-recursive functions will accumulate stack frames. For Node.js, always prefer iteration over recursion for performance-critical loops.
Asynchronous recursion:
Async functions and Promises can also cause stack issues if chained deeply without proper handling:
// Risky: deeply chained promises
function recursivePromise(n) {
if (n === 0) return Promise.resolve();
return Promise.resolve().then(() => recursivePromise(n - 1));
}
recursivePromise(10000); // Can cause stack overflowUse iteration with async/await instead:
async function iterativeLoop(count) {
for (let i = 0; i < count; i++) {
await somethingAsync();
}
}Worker threads for heavy computation:
If you have legitimate deep recursion that can't be refactored to iteration, consider moving it to a Worker thread, which has its own separate stack space.
Debugging tools:
- Use console.trace() to print the call stack at any point
- Enable V8 code coverage to see which functions are called most frequently
- Use Node.js profiler: node --prof script.js followed by node --prof-process to analyze hot functions
- Use the --abort-on-uncaught-exception flag to generate core dumps for analysis
Frequently asked questions
- What does "RANGE_ERROR" mean?
- A stack overflow happens when the call stack (a limited memory area that tracks function calls) becomes full. Every time a function is called, a new frame is added to the stack. js, there is a hard limit to how many frames can exist at once (typically around 10,000-30,000 depending on the system).
- How do I fix "RANGE_ERROR"?
- This error occurs when a function calls itself recursively without a proper base case or termination condition, exhausting the call stack. Stack overflow can also happen during deep object operations, circular event listeners, or when processing deeply nested data structures.
- What causes "RANGE_ERROR"?
- Recursive function missing base case or termination condition Recursive function base case never being reached due to logic error Event emitter triggering its own listener, creating infinite recursion
- Is the "RANGE_ERROR" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚Node.js Official Documentation
- 📖Node.js Errors Documentation - RangeError(Official Docs)
- 🐙
- 📝
- 📝