"Maximum call stack size exceeded" means your JavaScript program has exhausted the call stack's fixed memory limit, usually from unbounded recursion or an infinite synchronous loop.
What "Maximum call stack size exceeded" Means
The call stack is a finite LIFO (last-in, first-out) structure that tracks function calls. Every nested call pushes a frame; when the stack exceeds its limit (typically ~10,000 frames in V8, which powers Chrome and Node.js), the runtime throws this RangeError. It's not a syntax error — your code is syntactically valid, but the execution path never terminates.
Why It Happens
Three real causes dominate:
- Unbounded recursion — a function calls itself without a proper base case or with a base case that never becomes true.
- Infinite synchronous loops — not
forloops (those don't use the stack), but recursive functions masquerading as loops, like awhile(true)implemented via self-calls. - Deeply nested data structures — e.g., a circular JSON object passed to
JSON.stringifyor a recursive traversal of a cyclic graph without a visited set.
Example Code That Triggers It
Here's a minimal, runnable snippet that throws the error in Node.js or any browser console:
function countDown(n) {
// No base case — n keeps decreasing forever
return countDown(n - 1);
}
countDown(10);
// RangeError: Maximum call stack size exceeded
This crashes because countDown calls itself with no termination condition. Each call adds a frame; after ~10,000 frames, V8 throws.
How to Fix It
Add a base case and convert to iteration where possible:
function countDown(n) {
if (n <= 0) return; // base case stops recursion
console.log(n);
countDown(n - 1);
}
// Or better — avoid recursion entirely for linear work:
function countDownIterative(n) {
for (let i = n; i > 0; i--) {
console.log(i);
}
}
The fix works because the base case guarantees the stack eventually unwinds. The iterative version sidesteps the stack entirely, using constant memory regardless of n.
Common Mistakes That Cause This
Mistake 1: Forgetting to update the recursive argument. Developers write return fib(n) instead of return fib(n - 1) + fib(n - 2), creating infinite recursion. Always verify the recursive call moves toward the base case.
Mistake 2: Recursing on mutable shared state. A function that mutates a global counter but checks it after the recursive call — the check never runs because the stack overflows first. Check conditions before recursing, not after.
When Should You Worry About This?
Worry immediately if it appears in production code — it's a crash, not a warning. But if it shows up during development, it's almost always a logic bug, not a resource problem. Legitimate deep recursion (e.g., traversing a 50,000-node tree) should be rewritten iteratively or with an explicit stack array. If you're using React or Next.js, this error often surfaces in useEffect with recursive state updates — check for missing dependency arrays that cause infinite re-renders.
First thing to check next time: trace your recursive function's base case with a console.log at the top — if it never logs the exit condition, that's your bug.