Each Recursive Call Pushes a Frame — Overflow Triggers RangeError
The Call Stack
The call stack holds one frame per active function invocation; deep recursion fills it until V8 throws RangeError: Maximum call stack size exceeded.
What you'll learn
- Describe how stack frames are pushed and popped during a recursive call chain
- Reproduce and diagnose a stack overflow using console.trace
- Adjust V8 stack size with --stack-size and understand its practical limits
Every time a function is called, the JavaScript engine (V8 in Node.js) creates a stack frame — a record that stores the function’s local variables, its parameters, and the return address (where execution resumes when the function returns). These frames are pushed onto the call stack in LIFO order and popped off when the function returns.
How Frames Stack Up
Consider factorial(3):
Call stack (top = most recent)
────────────────────────────────
factorial(0) ← top (about to return 1)
factorial(1) ← waiting for factorial(0)
factorial(2) ← waiting for factorial(1)
factorial(3) ← waiting for factorial(2)
main ← original caller
────────────────────────────────
Each frame exists simultaneously in memory. Only when the top frame returns does the one below it resume.
Stack Overflow: RangeError
V8 limits the call stack to roughly 10 000 frames (exact number depends on
frame size and the --stack-size flag). Infinite or excessively deep recursion
exhausts this budget:
function infinite(n) {
return infinite(n + 1); // no base case — never stops
}
try {
infinite(0);
} catch (e) {
console.log(e instanceof RangeError); // true
console.log(e.message); // "Maximum call stack size exceeded"
} Diagnosing the Depth: console.trace
console.trace() prints the current call stack, which is useful for spotting
unexpectedly deep recursion during development:
function countdown(n) {
if (n === 0) {
console.trace("reached zero"); // prints full call chain
return;
}
countdown(n - 1);
}
countdown(5); In production, wrap suspicious recursive code in try/catch and log
error.stack — it contains the same information as a formatted string.
Adjusting V8 Stack Size
You can raise (or lower) the stack limit with the --stack-size flag, which
accepts a value in kilobytes:
node --stack-size=65536 app.js # ~65 MB stack instead of the default ~8 MB
This is occasionally useful for processing very deep JSON or XML trees, but it is rarely the right long-term fix — converting to an iterative approach with an explicit stack data structure is almost always safer and faster.
Stack Depth vs. Input Size
Input n | Frames on stack | Safe in V8? |
|---|---|---|
| 100 | 101 | Yes |
| 1 000 | 1 001 | Yes |
| 10 000 | 10 001 | Maybe |
| 100 000 | 100 001 | No |
Knowing that V8 caps around 10 000 frames tells you when you need to switch from recursion to an explicit stack-based loop — a pattern covered later in the tail-recursion lesson.
Up Next
Now that you understand the stack, learn how to design the base cases that prevent it from overflowing.
Designing Base Cases →