The Call Stack

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.

4 min read Level 2/5 #dsa#recursion#call-stack
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 nFrames on stackSafe in V8?
100101Yes
1 0001 001Yes
10 00010 001Maybe
100 000100 001No

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 →