javaseverity: critical
StackOverflowError

Java StackOverflowError — Infinite or excessively deep recursion

StackOverflowError: thread call stack exhausted

90% fixable~20 mindifficulty: intermediate

Verified against OpenJDK 21 documentation, JVM Specification §2.5.2, Effective Java 3rd edition · Updated June 2026

> quick_fix

Find the recursive method in the stack trace (it will appear dozens of times). Identify the missing or unreachable base case and add it, or convert the recursion to an iterative loop using an explicit stack.

// BROKEN: missing base case
int factorial(int n) {
    return n * factorial(n - 1); // never stops
}

// FIXED: base case added
int factorial(int n) {
    if (n <= 1) return 1; // base case
    return n * factorial(n - 1);
}

// ITERATIVE alternative for very deep inputs
int factorialIterative(int n) {
    int result = 1;
    for (int i = 2; i <= n; i++) result *= i;
    return result;
}

What causes this error

StackOverflowError is thrown when the JVM's thread stack runs out of space. Each method call pushes a new frame onto the stack containing local variables, parameters, and the return address. Infinite recursion (a method that calls itself with no terminating condition) or mutual recursion (A calls B calls A) fills the stack until there is no room for another frame.

> advertisementAdSense placeholder

How to fix it

  1. 01

    step 1

    Identify the repeating method in the stack trace

    The stack trace for a StackOverflowError is very long and shows the same method name repeated hundreds of times. That method (or pair of mutually recursive methods) is the source of the infinite recursion.

  2. 02

    step 2

    Find the missing or unreachable base case

    Every recursive method must have a base case — a condition under which it returns without calling itself. Check: (a) does the base case exist? (b) can the input ever actually reach it? If n is never decremented, the base case `if (n == 0)` is unreachable.

  3. 03

    step 3

    Check for circular object references

    Recursive serialization (toString, equals, hashCode) on objects with circular references causes StackOverflowError. For example, if Employee has a `manager` field pointing to another Employee, a naive toString() will recurse forever. Break the cycle by printing only the ID, not the full object.

  4. 04

    step 4

    Convert deep recursion to iteration

    If the recursion depth is legitimately large (e.g., traversing a deep tree with 10,000 nodes), convert to an iterative algorithm using an explicit `Deque<>` as a stack. This moves the stack from the JVM thread stack (limited) to the heap (much larger).

  5. 05

    step 5

    Increase stack size only as a temporary measure

    You can increase the thread stack size with `-Xss4m` JVM flag, but this only delays the error. It is not a fix — it just allows deeper recursion before the error recurs. Use it only to buy time while you refactor.

How to verify the fix

  • Run the same input — StackOverflowError should not recur
  • Test with deep but valid inputs (e.g., a linked list of 100,000 nodes) to confirm the iterative version handles them
  • Check serialization/logging of domain objects for any remaining circular-reference recursion

Why StackOverflowError happens at the runtime level

The JVM allocates a fixed-size stack region for each thread at creation time. Every method invocation pushes a stack frame containing the operand stack, local variable table, frame data, and return address onto this region. When a method calls itself recursively without converging toward a base case, frames accumulate faster than they are popped, and eventually the thread's stack region is exhausted. At that point the JVM raises StackOverflowError — a subclass of VirtualMachineError — signalling that the execution environment itself is compromised.

Common debug mistakes for StackOverflowError

  • Implementing equals() on an entity that calls equals() on a collection field that contains the same entity type
  • Writing toString() that prints a bidirectional relationship (parent.toString() calls child.toString() calls parent.toString())
  • A recursive descent parser with no memoization processing left-recursive grammar rules
  • Forgetting that Java does not do tail-call optimization, so a tail-recursive Fibonacci will still overflow on large inputs

When StackOverflowError signals a deeper problem

StackOverflowError in production almost always indicates a design problem: either a recursive data structure (tree, graph, linked list) is deeper than expected, or a domain model has circular references that a naive recursive visitor does not handle. The architectural fix is to audit domain objects for bidirectional relationships and ensure that recursive algorithms either have a bounded depth guarantee or are implemented iteratively. Frameworks like Jackson handle circular JSON serialization via @JsonManagedReference/@JsonBackReference — apply similar thinking to your own recursive traversals.

Editor's take

StackOverflowError in Java means the JVM's call stack has exceeded its maximum depth — almost always because of unbounded recursion where a method calls itself (directly or indirectly) without a proper base case. Unlike most Java exceptions, StackOverflowError is an `Error`, not an `Exception`, which means it signals a fundamental JVM resource exhaustion rather than a recoverable application condition. Catching it in a try-catch technically works but is unreliable because the stack may be so full that the catch block itself can't execute. The default JVM stack size is typically 512KB to 1MB per thread (platform-dependent), which allows roughly 5,000-15,000 stack frames for typical methods. The most dangerous production variant isn't simple `factorial(n)` recursion — it's circular object graphs that trigger `toString()` or `hashCode()` loops: Entity A references Entity B which references Entity A, and calling `System.out.println(entityA)` overflows. JPA and Hibernate entities are notorious for this when bidirectional relationships are serialized without `@JsonIgnore` or `@ToString.Exclude`. The fix for legitimate deep recursion is converting to an iterative approach with an explicit stack data structure. For circular reference issues, break the cycle with `@JsonManagedReference`/`@JsonBackReference` or implement `toString()` manually. Increasing `-Xss` is a band-aid that trades thread memory for headroom.

By Bikram Nath · Curator · Updated June 2026

Frequently asked questions

Does Java support tail-call optimization?

No. The JVM does not perform tail-call optimization. Even a perfectly written tail-recursive method will exhaust the stack on deep inputs. This is a known JVM limitation; Scala and Kotlin work around it with the @tailrec annotation (which compiles tail recursion to a loop at the bytecode level). In Java, you must manually convert tail recursion to a loop.

Can I catch StackOverflowError?

Technically yes — it is a Throwable. But in practice the JVM state may be corrupted when it is thrown, and catching it to continue execution is unreliable. The correct fix is always to eliminate the infinite recursion.

How deep can the Java call stack go?

The default thread stack size is platform-dependent: typically 256KB–1MB on the JVM. A simple recursive call with no local variables can go roughly 5,000–10,000 levels deep before hitting StackOverflowError. Methods with many local variables consume more stack space per frame, so they overflow sooner.

disclosure:Errordex runs AdSense, has zero third-party affiliate or sponsored links, and occasionally links to the editor’s own paid digital products (clearly labelled). Every fix is cross-referenced against the official sources listed in the “sources” sidebar before it ships. If a fix here didn’t work for you, please email so we can update the page.