Diagnosing and Fixing StackOverflowError in Scala Recursion
A concise diagnostic guide to identify, understand, and resolve StackOverflowError caused by deep, non‑tail‑recursive functions in Scala. Includes checklists, refactor patterns, and verification steps.
17 Jul 2026, 09:46 UTC

Problem Overview
When a Scala application processes large data sets with recursive functions, the JVM may throw java.lang.StackOverflowError. The error appears as a stack trace that ends with “StackOverflowError” and typically points to the recursive function itself. It indicates that the call stack has exceeded the JVM’s per‑thread stack limit (default ~1 MB on many platforms).
Root Causes
- Recursive call is not the final action (non‑tail).
- Mutual recursion between two or more functions.
- Large input size that forces many stack frames.
Diagnostic Checklist
| Check | What to Look For | How to Verify |
|---|---|---|
| Identify recursive functions | Search for def foo(...): containing a call to foo or another function that eventually calls back. | Use IDE “Find” or grep for “foo(” inside the file. |
| Is the recursive call the very last expression in the function body? | Check if there is any arithmetic, concatenation, or other operation after the call. | |
| Do two functions call each other? | Trace the call chain manually or use a static analyzer. | |
| How many elements are processed? | Measure with a counter or log the input length before recursion starts. |
Fixes Tied to Findings
- Refactor to Tail Recursion
- Move any computation after the recursive call into an accumulator parameter.
- Example (non‑tail):
def sum(xs: List[Int]): Int = xs match { case Nil => 0 case h :: t => h + sum(t) } - Tail‑recursive version:
import scala.annotation.tailrec @tailrec def sum(xs: List[Int], acc: Int = 0): Int = xs match { case Nil => acc case h :: t => sum(t, acc + h) } - Compile‑time check: the
@tailrecannotation forces the compiler to reject non‑tail‑recursive functions.
- Use Trampoline for Mutual Recursion or Complex Branching
- Wrap recursive calls in
TailCalls.import scala.util.control.TailCalls.{TailRec, Done, tailcall} def fib(n: Int): TailRec[Int] = n match { case 0 => Done(0) case 1 => Done(1) case _ => tailcall(fib(n - 1)).flatMap(a => tailcall(fib(n - 2)).map(b => a + b)) } // Execute val result = fib(40).result - Trampolines turn the recursion into a loop internally, preventing stack growth.
- Wrap recursive calls in
- Adjust JVM Stack Size (Temporary Work‑Around)
- Run the JVM with
-Xss2mto double the stack size. This does not address algorithmic complexity and may hide deeper issues. - Use only for quick testing, not production.
- Run the JVM with
- Iterative Replacement
- Rewrite the algorithm using loops (for, while) or collections methods (foldLeft, foldRight) that are inherently stack‑safe.
- Example: replace a recursive tree traversal with a stack‑based depth‑first search.
Verification Steps
- Compile with
@tailrecannotation. A non‑tail‑recursive function should produce a compilation error: “tail recursive function does not terminate”. - Run a unit test with a large input (e.g.,
List.range(0, 10000)) and assert that noStackOverflowErroris thrown. - Profile the application to confirm that the stack depth remains shallow (use JVM tools like jvisualvm or
jstack).
Escalation Criteria
- If refactoring to tail recursion or trampoline still results in a stack overflow, the algorithm may have exponential complexity. Consider algorithmic redesign.
- When mutual recursion cannot be refactored to a single tail‑recursive function, a custom trampoline or iterative approach is mandatory.
- If the stack size increase resolves the issue but performance degrades, revisit the data structure or algorithm for a more efficient solution.
By following this diagnostic flow, you can systematically pinpoint the cause of StackOverflowError in Scala and apply the most appropriate fix—tail recursion, trampolines, or iterative redesign—while ensuring the solution is verified and stable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.