RangeError: Maximum call stack size exceeded

Quick answer

The call stack overflowed — almost always infinite or too-deep recursion. Add a base case that's actually reached, watch for accidental self-calls (getters/setters, event re-dispatch, React render loops) and circular references, and for very deep data convert the recursion to iteration with an explicit stack.

The exact error string

function f() { return f(); }
f();
// Uncaught RangeError: Maximum call stack size exceeded

// Node prints a long repeated stack trace ending in:
// RangeError: Maximum call stack size exceeded

How to find the recursion

A stack overflow trace is the diagnosis: the same one or two frames repeat hundreds of times — that repeating frame is your loop, and the bottom of the trace is where it started. If the trace is truncated, widen it before you re-run:

Error.stackTraceLimit = 100;     // show more frames to spot the cycle
// or run: node --stack-trace-limit=100 app.js

// is it direct or indirect? log on entry to suspects:
function walk(n) { console.count("walk"); /* ... */ walk(next); }
// a runaway count pinpoints the function that never returns

Cause 1: missing or unreachable base case

// ❌ no base case — recurses forever
function countdown(n) { console.log(n); countdown(n - 1); }

// ✅ stop when the condition is met, and move toward it
function countdown(n) {
  if (n < 0) return;             // base case
  console.log(n);
  countdown(n - 1);              // progresses toward the base case
}

Cause 2: accidental self-call

const obj = {
  get value() { return this.value; }   // ❌ getter reads itself -> recursion
};

el.addEventListener("click", function handler() {
  el.click();                          // ❌ re-dispatches the same event
});

Cause 3: circular references when walking objects

function walk(node, seen = new Set()) {
  if (seen.has(node)) return;          // ✅ stop on a cycle
  seen.add(node);
  for (const k in node) {
    if (node[k] && typeof node[k] === "object") walk(node[k], seen);
  }
}

Cause 4: deep but finite recursion → use iteration

// iterative depth-first traversal — no recursion, no stack limit
function walkIter(root) {
  const stack = [root];
  while (stack.length) {
    const node = stack.pop();
    for (const k in node) {
      if (node[k] && typeof node[k] === "object") stack.push(node[k]);
    }
  }
}

Worked example: recursing over nested JSON with a cycle

You walk a parsed JSON-like object to transform it, but an object references an ancestor (built in memory, or a graph serialized with refs), so the recursion never bottoms out:

const a = { name: "a" };
a.self = a;                       // a cycle

function deepCount(node) {        // ❌ overflows on the cycle
  let n = 1;
  for (const k in node) if (typeof node[k] === "object") n += deepCount(node[k]);
  return n;
}

function deepCount(node, seen = new Set()) {   // ✅ track visited
  if (seen.has(node)) return 0;
  seen.add(node);
  let n = 1;
  for (const k in node) if (node[k] && typeof node[k] === "object") n += deepCount(node[k], seen);
  return n;
}

Inspect a suspect structure in the JSON Formatter first — and remember that JSON.stringify on a cycle throws the clearer Converting circular structure to JSON, not this error.

In React: render loops

// ❌ setState during render re-triggers render endlessly
function C() {
  const [n, setN] = useState(0);
  setN(n + 1);                    // -> "Too many re-renders" / stack overflow
}

// ❌ effect with a dependency it also updates, every render
useEffect(() => { setN(n + 1); }, [n]);

// ✅ update in response to an event or a one-time effect
useEffect(() => { setN(1); }, []);   // runs once

Common variants of this message

CauseTell-taleFix
missing base caseone frame repeatsadd a reachable base case
circular objectrepeats while walking datatrack visited in a Set
getter/setter self-refframe is a property accessorbreak the self-reference
deep finite recursiononly fails on large inputconvert to iteration
React render loop"Too many re-renders"don't setState during render
JSON.stringify on a cycledifferent messagesee circular structure

Debugging checklist

Frequently Asked Questions

How do I find the function that's recursing?

Look at the stack trace: a stack overflow shows the same one or two frames repeated hundreds of times — that repeating frame is the loop. The bottom of the trace shows where it started. If it's truncated, set a higher trace limit (Error.stackTraceLimit) or step through in the debugger to see the cycle.

Is this the same as a stack overflow?

Yes. 'Maximum call stack size exceeded' is JavaScript's stack overflow: too many nested function calls that never return. Each call uses a stack frame, and the engine has a fixed limit (a few thousand to tens of thousands of frames depending on the engine and frame size).

Why does it appear with JSON.stringify?

Usually it doesn't — JSON.stringify on a circular object throws a different, clearer error: 'Converting circular structure to JSON'. If JSON.stringify overflows the stack instead, the structure is extremely deeply nested rather than circular; serialize in chunks or flatten it.

How is this different from React's 'Too many re-renders'?

They're related infinite loops with different messages. 'Too many re-renders' comes from calling setState during render; 'Maximum call stack size exceeded' is a function calling itself synchronously. A setState in render or a missing useEffect dependency can cause either, depending on timing — both are fixed by breaking the self-trigger.

Will increasing the stack size fix it?

Rarely, and only for genuinely deep but correct recursion. node --stack-size=... raises the limit but just delays the overflow and can crash the process harder. Fix the base case or convert to iteration; reserve a larger stack for cases you truly can't refactor.

Why does it only crash on large inputs?

Correct recursion still overflows once the depth exceeds the stack limit. Small inputs stay under it; a deeply nested object, a long linked list, or a big tree pushes past it. Convert that algorithm to an iterative version with an explicit stack so depth is bounded by heap memory, not the call stack.

Recursing over JSON data?

Inspect deeply-nested or self-referencing structures in the formatter before you walk them — nothing is uploaded to a server.

JSON Formatter JSON Diff All Error References
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.