StackOverflowError, explained and fixed

java.lang.StackOverflowError is thrown when a thread's stack, the memory holding its chain of method calls, is exhausted by calls nesting too deeply. In Java this is overwhelmingly a recursion bug: a method that calls itself, directly or through a cycle of methods, without a termination condition that the actual arguments ever satisfy. The stack trace that comes with it looks alarming (it can be thousands of lines), but its repeating pattern is precisely the diagnostic.

How to read the evidence

  1. Look for the repeating block. The trace repeats the same few frames in a cycle; those methods are the loop. The class and line numbers inside the cycle are where to work, not the top of the trace.
  2. Check the arguments conceptually: trace by hand what changes on each recursive call. If nothing shrinks toward the exit condition (the collection is not smaller, the counter does not move, the node is not deeper), the recursion cannot end.

The usual causes

  1. A base case that is missing, wrong, or checked after the recursive call, including equality tests the values skip over (counting by twos toward an odd target, for instance).
  2. Accidental self-reference in data: a tree or graph node that (transitively) contains itself, traversed or serialized (a toString, an equals/hashCode, or a JSON serializer walking the cycle) without a visited set.
  3. Mutual delegation loops: two objects that forward a call to each other, a wrapper calling the wrapped object that was mistakenly set to the wrapper itself, or equals/hashCode implementations calling each other across classes.
  4. Genuinely deep but finite recursion on large inputs, deep trees or long chains, where the logic is correct and the default thread stack (commonly 512 KB to 1 MB) is simply small.

The fixes, in order

  1. Repair the termination: make the exit condition reachable and tested first, and make every recursive step move toward it demonstrably (smaller input, next node, decremented depth).
  2. Break data cycles with a visited set (an IdentityHashMap-based set for traversal and serialization), or exclude the back-reference from serialization, the standard treatment for parent pointers in JSON.
  3. Convert deep linear recursion to a loop when the depth scales with input size; an explicit stack (an ArrayDeque) on the heap handles depths no thread stack will.
  4. Only for verified-finite depth, raise the thread stack with -Xss2m. Treat needing this as a design smell: if the depth depends on user data, someone will eventually supply deeper data.

Common questions

Why is the stack trace truncated or repetitive? The JVM prints the frames it captured; a recursion cycle produces thousands of near-identical frames and tools truncate them. The repetition is the signal: find the cycle and the two or three distinct frames inside it.

Can a StackOverflowError corrupt data? It can interrupt an operation midway, leaving partially updated structures, which is why it should be treated as a serious bug rather than caught and ignored. Catching it to “keep the app alive” is rarely safe.

How is this different from the JavaScript maximum call stack error? Same underlying mechanism, different platform and wording. The diagnosis transfers directly: find the repeating frames, repair the termination condition.

Related errors

OutOfMemoryError: Java heap space · Maximum call stack size exceeded (JavaScript) · NullPointerException