System.StackOverflowException
A stack overflow happens when the call stack runs out of space, usually because a method calls itself without an end. .NET cannot recover from it. The runtime writes Stack overflow and the repeated frames to the console, and the process ends.
You can not catch a StackOverflowException, so there is no exception message in your log in most cases. What you see is a crashed process, an application pool that restarts, or a container that exits. The output of the process and the Windows event log have the frames.
Minimum version: >= 1.1 >= Core 2.0
Statistics
Common causes
The cause is nearly always unbounded recursion, and often one that is not obvious in the code. These are the typical ones.
A recursive method without an end
Each call uses stack space, and a method that does not reach its base case runs until the stack is full. The default stack of a thread is 1 MB on Windows, so it takes around 10,000 to 25,000 calls.
public static int GetDepth(int level) => GetDepth(level + 1) + 1; // never returns, ends the process
Add a base case that always is reached, or make the algorithm iterative with an explicit stack:
public static int GetDepth(IReadOnlyDictionary<int, int> parents, int category)
{
var depth = 0;
while (parents.TryGetValue(category, out var parent))
{
category = parent;
depth++;
}
return depth;
}
A property that returns itself
A property that reads or writes its own name, instead of the field behind it, calls itself. It compiles without a warning and fails the first time it is used.
public class Product
{
public string Name
{
get { return Name; } // calls the getter again
set { Name = value; } // calls the setter again
}
}
Use a field, or an auto-property that does it for you:
public class Product
{
public string Name { get; set; }
}
Operators and overrides that call themselves
Inside ==, Equals, GetHashCode and ToString, using the same operator on the same type calls the method again. A common case is comparing to null with == inside the == operator.
public class Money
{
public decimal Amount { get; set; }
public static bool operator ==(Money a, Money b)
{
if (a == null) return b == null; // calls this operator again
return b != null && a.Amount == b.Amount;
}
public static bool operator !=(Money a, Money b) => !(a == b);
}
Compare with is null, which does not use the operator:
public class Money
{
public decimal Amount { get; set; }
public static bool operator ==(Money a, Money b)
{
if (a is null) return b is null;
return b is not null && a.Amount == b.Amount;
}
public static bool operator !=(Money a, Money b) => !(a == b);
public override bool Equals(object obj) => obj is Money m && this == m;
public override int GetHashCode() => Amount.GetHashCode();
}
Object graphs with cycles or great depth
Serializers, mappers and custom code that walk an object graph recurse into every reference. If an order points to a customer that points back to the order, the walk never ends. Very deep structures, such as a long linked list or deeply nested JSON from a user, can fill the stack without a cycle.
Configure the serializer to ignore or preserve references, limit the depth for input from outside, and walk deep structures with a loop.
How to fix it and prevent it
Make the recursion end
Write down the base case first, and check that every path reaches it. For input that you do not control, such as a tree from a user, pass a depth along and stop at a limit.
public static int Count(IReadOnlyDictionary<int, List<int>> children, int node, int depth = 0)
{
if (depth > 1000) throw new InvalidOperationException("The tree is too deep");
var total = 1;
foreach (var child in children.GetValueOrDefault(node) ?? new List<int>())
{
total += Count(children, child, depth + 1);
}
return total;
}
Use a loop and your own stack
Any recursive algorithm can be written with a Stack<T> on the heap, which is limited by memory and not by the 1 MB of the thread.
public static int Count(IReadOnlyDictionary<int, List<int>> children, int root)
{
var pending = new Stack<int>();
pending.Push(root);
var total = 0;
while (pending.Count > 0)
{
var node = pending.Pop();
total++;
foreach (var child in children.GetValueOrDefault(node) ?? new List<int>())
{
pending.Push(child);
}
}
return total;
}
Find it from the repeated frames
The runtime prints the repeated frames when the process ends. The method or property in that block is the one that recurses. In a dump, the same frames appear thousands of times.
How to read the output
There is no exception, only the text the runtime writes before it ends the process. It names the frames that repeat:
Stack overflow.
Repeated 24104 times:
--------------------------------
at Shop.Services.CategoryTree.GetDepth(System.Int32)
--------------------------------
at Program.<Main>$(System.String[])
The method CategoryTree.GetDepth was repeated 24,104 times before the stack was full, and the call that started it is the one below. The cause is in that method, and in the way it calls itself. When the process is hosted in IIS or a container, look for the same text in the stdout log, or in the Windows event log under Application.
Should you catch it?
You cannot. Since .NET Framework 2.0, the process ends and no catch or finally block runs. The only protection is to write code that cannot recurse without a limit.
To see these crashes, collect the output of the process, or use a monitoring tool that records crashes of the host. For ASP.NET Core, a log of the standard output or a crash dump is the most reliable source.
Find StackOverflowException before your users do
elmah.io logs every unhandled exception in your .NET application with its stack trace and request details, groups identical errors and notifies you when a new one appears.
Start free trialRelated exceptions
- OutOfMemoryException is about the heap, and can be caught.
- TypeInitializationException wraps an exception from a static constructor.
- AccessViolationException is another error that ends the process in most cases.
Frequently asked questions
Can I catch StackOverflowException?
No. The process is ended by the runtime. A try/catch does not help, and neither does [HandleProcessCorruptedStateExceptions], which does not work for this in current versions of .NET.
How deep can recursion be?
The default stack is 1 MB for most threads, 8 MB for the main thread on Linux. A simple method can recurse 10,000 to 50,000 times. A method with many locals uses more space for each call.
Can I increase the stack size?
You can create a thread with a larger stack with the maxStackSize parameter. It moves the limit, but does not fix a recursion that never ends.
How do I find which code caused a crash?
Look at the repeated frames in the output, or open a crash dump in Visual Studio. On Windows, the event log has an Application Error and a .NET Runtime entry for the crash.
Let your AI agent track it down
Connect Claude Code, Cursor, VS Code or Visual Studio to the elmah.io MCP server and ask your agent to look into StackOverflowException for you. For example:
The agent reads the stack trace and request details from elmah.io, finds the code in your repository and proposes a fix. In Claude Code, add the server with one command:
claude mcp add --transport http --client-id claudecode elmahio https://mcp.elmah.io/mcp
The MCP server is included on every plan and is currently in beta. Set up the MCP server.
Further reading
YouTube videos
Answers from Stack Overflow
You can see the "official" reason in the last comment here.
Posted by Microsoft on 14/11/2008 at 19:52
Thanks for the suggestion for Visual Studio!
You are right that we could easily detect property recursion, but we can't guarantee that there is nothing useful being accomplished by the recursion. The body of the property could set other fields on your object which change the behavior of the next recursion, could change its behavior based on user input from the console, or could even behave differently based on random values. In these cases, a self-recursive property could indeed terminate the recursion, but we have no way to determine if that's the case at compile-time (without solving the halting problem!).
For the reasons above (and the breaking change it would take to disallow this), we wouldn't be able to prohibit self-recursive properties.
Alex Turner
Program Manager
Visual C# Compiler
By Brandon. Read the original answer on Stack Overflow.
Stack overflows
I'll make it easy for you; but this is actually quite complex... Note that I will generalize quite a bit here.
As you might know, most languages use the stack for storing call information. See also: https://msdn.microsoft.com/en-us/library/zkwh89ks.aspx for how cdecl works. If you call a method, you push stuff on the stack; if you return, you pop stuff from the stack.
Note that recursion isn't normally 'inlined'. (Note: I explicitly say 'recursion' here and not 'tail recursion'; the latter works like a 'goto' and doesn't grow the stack).
The easiest way to detect a stack overflow is to check your current stack depth (e.g. bytes used) - and if it hits a boundary, give an error. To clarify about this 'boundary check': the way these checks are done is normally by using guard pages; this means boundary checks aren't normally implemented as if-then-else checks (although some implementations exist...).
In most languages, each thread has its own stack.
Detecting infinite loops
Well now, here's a question I haven't heard for a while. :-)
Basically detecting all infinite loops requires you to solve the Halting Problem. Which is by the way an undecidable problem. This is definitely not done by compilers.
This doesn't mean you can't do any analysis; in fact, you can do quite a bit of analysis. However, also note that sometimes you want things to run indefinitely (such as the main loop in a web server).
Other languages
Also interesting... Functional languages use recursion, so they are basically bound by the stack. (That said, functional languages also tend to use tail recursion, which works more or less like a 'goto' and don't grow the stack.)
And then there's Logical languages.. well now, I'm not sure how to loop forever in that - you'll probably end up with something that won't evaluate at all (no solution can be found). (Though, this probably depends on the language... )
Yielding, async, continuations
An interesting concept is you might think of is called continuations. I've heard from Microsoft that when yield was first implemented, real continuations were considered as implementation. Continuations basically allow you to 'save' the stack, continue somewhere else and 'restore' the stack back at a later point... (Again, the details are much more complicated than this; this is just the basic idea).
Unfortunately Microsoft didn't go for this idea (although I can imagine why), but implemented it by using a helper class. Yield and async in C# work by adding a temporary class and interning all the local variables within the class. If you call a method that does a 'yield' or 'async', you actually create a helper class (from within the method you call and push on the stack) that's pushed on the heap. The class that's pushed on the heap has the functionality (e.g. for yield this is the enumeration implementation). The way this is done is by using a state variable, which stores the location (e.g. some state id) where the program should continue when MoveNext is called. A branch (switch) using this ID takes care of the rest. Note that this mechanism does nothing 'special' with the way the stack works itself; you can implement the same by yourself using classes and methods (it just involves more typing :-)).
Solving stack overflows with a manual stack
I always like a good flood fill. A picture will give you a hell of a lot of recursion calls if you do this wrong... say, like this:
public void FloodFill(int x, int y, int color)
{
// Wait for the crash to happen...
if (Valid(x,y))
{
SetPixel(x, y, color);
FloodFill(x - 1, y, color);
FloodFill(x + 1, y, color);
FloodFill(x, y - 1, color);
FloodFill(x, y + 1, color);
}
}
There's nothing wrong with this code though. It does all the work, but our stack gets in the way. Having a manual stack solves this, even though the implementation is basically the same:
public void FloodFill(int x, int y, int color)
{
Stack<Tuple<int, int>> stack = new Stack<Tuple<int, int>>();
stack.Push(new Tuple<int, int>(x, y));
while (stack.Count > 0)
{
var current = stack.Pop();
int x2 = current.Item1;
int y2 = current.Item2;
// "Recurse"
if (Valid(x2, y2))
{
SetPixel(x2, y2, color);
stack.Push(new Tuple<int, int>(x2-1, y2));
stack.Push(new Tuple<int, int>(x2+1, y2));
stack.Push(new Tuple<int, int>(x2, y2-1));
stack.Push(new Tuple<int, int>(x2, y2+1));
}
}
}
By atlaste. Read the original answer on Stack Overflow.
There are a number of answers here already, many of which get the gist across, and many of which have subtle or large errors. Rather than try to explain the whole thing from scratch, let me just hit a few high points.
I am not sure how languages other than C# handle a stack overflow.
Your question is "how is a stack overflow detected?" Is your question about how it is detected in C#, or in some other language? If you have a question about another language, I recommend creating a new question.
I think it is not possible to say (for example) if the stack is 1000 calls deep, then throw the exception. Because maybe in some cases the correct logic will be that deep.
It is absolutely possible to implement a stack overflow detection like that. In practice, this is not how it is done, but there is no in-principle reason why the system could not have been designed that way.
What is the logic behind the detection of an infinite loop in my program?
You mean an unbounded recursion, not an infinite loop.
I'll describe it below.
I just added the stack-overflow tag to this question, and the description says it is being thrown when the call stack consumes too much memory. Does that mean the call stack is some sort of path to the current executing position of my program and if it cannot store more path information, then the exception is thrown?
Short answer: yes.
Longer answer: The call stack is used for two purposes.
First, to represent activation information. That is, the values of the local variables and temporary values whose lifetimes are equal to or shorter than the present activation ("call") of a method.
Second, to represent continuation information. That is, when I am done with this method, what do I need to do next? Note that the stack does not represent "where did I come from?". The stack represents where am I going next, and it just so happens that usually when a method returns, you go back to where you came from.
The stack also stores information for non-local continuations -- that is, exception handling. When a method throws, the call stack contains data that helps the runtime determine what code, if any, contains the relevant catch block. That catch block then becomes the continuation -- the "what do I do next" -- of the method.
Now, before I go on, I note that the call stack is a data structure that is being used for two purposes, violating the single responsibility principle. There is no requirement that there be one stack used for two purposes, and in fact there are some exotic architectures in which there are two stacks, one for activation frames and one for return addresses (which are the reification of continuation.) Such architectures are less vulnerable to "stack smashing" attacks that can occur in languages like C.
When you call a method, memory is allocated on the stack to store the return address -- what do I do next -- and the activation frame -- the locals of the new method. Stacks on Windows are by default of fixed size, so if there is not enough room, bad things happen.
In more detail, how does Windows do out of stack detection?
I wrote the out-of-stack detection logic for 32 bit Windows versions of VBScript and JScript in the 1990s; the CLR uses similar techniques as I used, but if you want to know the CLR-specific details, you'll have to consult an expert on the CLR.
Let's consider just 32 bit Windows; 64 bit Windows works similarly.
Windows uses virtual memory of course -- if you do not understand how virtual memory works, now would be a good time to learn before you read on. Each process is given a 32 bit flat address space, half reserved for the operating system and half for the user code. Each thread is by default given a reserved contiguous block of one megabyte of address space. (Note: this is one reason why threads are heavyweight. A million bytes of contiguous memory is a lot when you only have two billion bytes in the first place.)
There are some subtleties here regarding whether that contiguous address space is merely reserved or actually committed, but let's gloss those over. I'll continue to describe how it works in a conventional Windows program rather than going into the CLR details.
OK, so we have lets say a million bytes of memory, divided into 250 pages of 4kb each. But the program when it first starts running is only going to need maybe a few kb of stack. So here's how it works. The current stack page is a perfectly good committed page; it's just normal memory. The page beyond that is marked as a guard page. And the last page in our million byte stack is marked as a very special guard page.
Suppose we try to write a byte of stack memory beyond our good stack page. That page is guarded, so a page fault occurs. The operating system handles the fault by making that stack page good, and the next page becomes the new guard page.
However, if the last guard page is hit -- the very special one -- then Windows triggers an out-of-stack exception, and Windows resets the guard page to mean "if this page is hit again, terminate the process". If that happens then Windows terminates the process immediately. No exception. No cleanup code. No dialog box. If you've ever seen a Windows app just suddenly disappear completely, probably what happened was someone hit the guard page at the end of the stack for the second time.
OK, so now that we understand the mechanisms -- and again, I am glossing over many details here -- you can probably see how to write code that makes out-of-stack exceptions. The polite way -- which is what I did in VBScript and JScript -- is to do a virtual memory query on the stack and ask where the final guard page is. Then periodically look at the current stack pointer, and if it is getting within a couple of pages, simply create a VBScript error or throw a JavaScript exception right then and there rather than letting the operating system do it for you.
If you don't want to do that probing yourself, then you can handle the first chance exception that the operating system gives you when the final guard page is hit, turn that into a stack overflow exception that C# understands, and be very careful to not hit the guard page a second time.
By Eric Lippert. Read the original answer on Stack Overflow.
You did not properly implement the base case termination, which causes quicksort to never stop recursing into itself with sublists of length 0.
Change this:
if (low < high)
pivot_loc = partition(input, low, high);
quicksort(input, low, pivot_loc - 1);
quicksort(input, pivot_loc + 1, high);
to this:
if (low < high) {
pivot_loc = partition(input, low, high);
quicksort(input, low, pivot_loc - 1);
quicksort(input, pivot_loc + 1, high);
}
By Deestan. Read the original answer on Stack Overflow.
When setting the price property, you invoke the setter, which invokes the setter which invokes the setter, etc..
Solution:
public int _price;
public int price
{
get
{
return no * 5;
}
set
{
_price = value;
}
}
By Eyal Perry. Read the original answer on Stack Overflow.
Source: Stack Overflow