135

I have a recursive call to a method that throws a stack overflow exception. The first call is surrounded by a try catch block but the exception is not caught.

Does the stack overflow exception behave in a special way? Can I catch/handle the exception properly?

Not sure if relevant, but additional information:

  • the exception is not thrown in the main thread

  • the object where the code is throwing the exception is manually loaded by Assembly.LoadFrom(...).CreateInstance(...)

theduck
  • 2,589
  • 13
  • 17
  • 23
Toto
  • 7,491
  • 18
  • 50
  • 72
  • 3
    @RichardOD, sure I fix the bug because it was a bug. However the issue can appear in a different way and I wan to handle it – Toto Oct 21 '09 at 07:24
  • 8
    Agreed, a stack overflow is a serious error that can't be caught because it *shouldn't* be caught. Fix the broken code instead. – Ian Kemp Oct 21 '09 at 07:26
  • 12
    @RichardOD: If one wants to design e.g. a recursive-descent parser and not impose artificial limits on depth beyond those actually required by the host machine, how should one go about it? If I had my druthers, there would be a StackCritical exception which could be explicitly caught, which would be fired while there was still a little stack space left; it would disable itself until it was actually thrown, and could then not be caught until a safe amount of stack space remained. – supercat Dec 17 '10 at 18:54
  • 3
    This question is useful -- I want to fail a unit test if a stack overflow exception occurs -- but NUnit just moves the test to the "ignored" category instead of failing it like it would with other exceptions -- I need to catch it and do an `Assert.Fail` instead. So seriously -- how do we go about this? – BrainSlugs83 Sep 07 '14 at 20:04

10 Answers10

119

Starting with 2.0 a StackOverflow Exception can only be caught in the following circumstances.

  1. The CLR is being run in a hosted environment* where the host specifically allows for StackOverflow exceptions to be handled
  2. The stackoverflow exception is thrown by user code and not due to an actual stack overflow situation (Reference)

*"hosted environment" as in "my code hosts CLR and I configure CLR's options" and not "my code runs on shared hosting"

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 34
    If it can't be caught in any relevant scebario, why does the StackoverflowException object exist? – Manu Oct 27 '09 at 12:03
  • 10
    @Manu for at least a couple of reasons. 1) Is that it could be caught, kind of, in 1.1 and hence had a purpose. 2) It can still be caught if you are hosting the CLR so it's still a valid exception type – JaredPar Oct 27 '09 at 14:45
  • 3
    If it can't be caught... Why doesn't the windows event explaining what happened include the full stack trace by default? –  Mar 21 '14 at 14:34
  • 12
    How does one go about allowing StackOverflowExceptions to be handled in a hosted environment? Reason I'm asking is because I run a hosted environment, and I'm having this exact problem, where it destroys the entire app pool. I would much rather have it abort the thread, where it can unwind back up to the top, and I can then log the error and continue on without having all the apppool's threads killed. – Brain2000 Mar 31 '14 at 22:15
  • `Starting with 2.0 ...`, I'm curios, what is preventing them from catching SO and how it was possible `1.1` (you mentioned that in your comment)? – M.kazem Akhgary Oct 24 '17 at 16:54
  • @Brain2000 same requirement here – Weipeng Jul 10 '18 at 00:25
52

The right way is to fix the overflow, but....

You can give yourself a bigger stack:-

using System.Threading;
Thread T = new Thread(threadDelegate, stackSizeInBytes);
T.Start();

You can use System.Diagnostics.StackTrace FrameCount property to count the frames you've used and throw your own exception when a frame limit is reached.

Or, you can calculate the size of the stack remaining and throw your own exception when it falls below a threshold:-

class Program
{
    static int n;
    static int topOfStack;
    const int stackSize = 1000000; // Default?

    // The func is 76 bytes, but we need space to unwind the exception.
    const int spaceRequired = 18*1024; 

    unsafe static void Main(string[] args)
    {
        int var;
        topOfStack = (int)&var;

        n=0;
        recurse();
    }

    unsafe static void recurse()
    {
        int remaining;
        remaining = stackSize - (topOfStack - (int)&remaining);
        if (remaining < spaceRequired)
            throw new Exception("Cheese");
        n++;
        recurse();
    }
}

Just catch the Cheese. ;)

  • 55
    `Cheese` is far from specific. I'd go for `throw new CheeseException("Gouda");` – C.Evenhuis Nov 22 '11 at 14:29
  • 17
    @C.Evenhuis Whilst there's no doubt that Gouda is an exceptional cheese it should be a RollingCheeseException("Double Gloucester") really see http://www.cheese-rolling.co.uk/ –  Nov 24 '11 at 16:53
  • 4
    lol, 1) fixing is not possible because without catching it you often don't know where it happens 2) increasing the Stacksize is useless with **endless** recursion andm 3) checking the Stack in the right location is like the first – Firo Feb 12 '15 at 08:14
  • 6
    but I'm lactose intolerant – brianc Feb 02 '18 at 15:24
46

From the MSDN page on StackOverflowExceptions:

In prior versions of the .NET Framework, your application could catch a StackOverflowException object (for example, to recover from unbounded recursion). However, that practice is currently discouraged because significant additional code is required to reliably catch a stack overflow exception and continue program execution.

Starting with the .NET Framework version 2.0, a StackOverflowException object cannot be caught by a try-catch block and the corresponding process is terminated by default. Consequently, users are advised to write their code to detect and prevent a stack overflow. For example, if your application depends on recursion, use a counter or a state condition to terminate the recursive loop. Note that an application that hosts the common language runtime (CLR) can specify that the CLR unload the application domain where the stack overflow exception occurs and let the corresponding process continue. For more information, see ICLRPolicyManager Interface and Hosting the Common Language Runtime.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
31

As several users have already said, you can't catch the exception. However, if you're struggling to find out where it's happening, you may want to configure visual studio to break when it's thrown.

To do that, you need to open Exception Settings from the 'Debug' menu. In older versions of Visual Studio, this is at 'Debug' - 'Exceptions'; in newer versions, it's at 'Debug' - 'Windows' - 'Exception Settings'.

Once you have the settings open, expand 'Common Language Runtime Exceptions', expand 'System', scroll down and check 'System.StackOverflowException'. Then you can look at the call stack and look for the repeating pattern of calls. That should give you an idea of where to look to fix the code that's causing the stack overflow.

Simon
  • 25,468
  • 44
  • 152
  • 266
17

As mentioned above several times, it's not possible to catch a StackOverflowException that was raised by the System due to corrupted process-state. But there's a way to notice the exception as an event:

http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

Starting with the .NET Framework version 4, this event is not raised for exceptions that corrupt the state of the process, such as stack overflows or access violations, unless the event handler is security-critical and has the HandleProcessCorruptedStateExceptionsAttribute attribute.

Nevertheless your application will terminate after exiting the event-function (a VERY dirty workaround, was to restart the app within this event haha, havn't done so and never will do). But it's good enough for logging!

In the .NET Framework versions 1.0 and 1.1, an unhandled exception that occurs in a thread other than the main application thread is caught by the runtime and therefore does not cause the application to terminate. Thus, it is possible for the UnhandledException event to be raised without the application terminating. Starting with the .NET Framework version 2.0, this backstop for unhandled exceptions in child threads was removed, because the cumulative effect of such silent failures included performance degradation, corrupted data, and lockups, all of which were difficult to debug. For more information, including a list of cases in which the runtime does not terminate, see Exceptions in Managed Threads.

FooBarTheLittle
  • 459
  • 4
  • 7
7

Yes from CLR 2.0 stack overflow is considered a non-recoverable situation. So the runtime still shut down the process.

For details please see the documentation http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • From CLR 2.0 a `StackOverflowException` terminates the process by default. – Brian Rasmussen Jun 26 '15 at 16:23
  • No. You can catch OOM and in some cases it might make sense to do so. I don't know what you mean by thread disappearing. If a thread has an unhandled exception the CLR will terminate the process. If your thread completes its method it will be cleaned up. – Brian Rasmussen Jun 26 '15 at 22:10
6

You can't. The CLR won't let you. A stack overflow is a fatal error and can't be recovered from.

Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • So how do you make a Unit Test fail for this exception if instead of being catchable it crashes the unit test runner instead? – BrainSlugs83 Sep 07 '14 at 20:07
  • 1
    @BrainSlugs83. You don't, because that's a silly idea. Why are you testing if your code fails with a StackOverflowException anyway? What happens if the CLR changes so it can handle a deeper stack? What happens if you call your unit tested function somewhere that already has a deeply nested stack? It seems like something that's not able to be tested. If you are trying to throw it manually, choose a better exception for the task. – Matthew Scharley Sep 10 '14 at 01:33
6

You can't as most of the posts are explaining, let me add another area:

On many websites you will find people saying that the way to avoid this is using a different AppDomain so if this happens the domain will be unloaded. That is absolutely wrong (unless you host your CLR) as the default behavior of the CLR will raise a KillProcess event, bringing down your default AppDomain.

No hay Problema
  • 853
  • 1
  • 10
  • 14
4

It's impossible, and for a good reason (for one, think about all those catch(Exception){} around).

If you want to continue execution after stack overflow, run dangerous code in a different AppDomain. CLR policies can be set to terminate current AppDomain on overflow without affecting original domain.

ima
  • 8,105
  • 3
  • 20
  • 19
  • 2
    The "catch" statements wouldn't really be a problem, since by the time a catch statement could execute the system would have rolled back the effects of whatever had tried to use two much stack space. There's no reason catching stack overflow exceptions would have to be dangerous. The reason they're such exceptions can't be caught is that allowing them to be caught safely would require adding some extra overhead to *all* code that uses the stack, even if it doesn't overflow. – supercat Dec 05 '13 at 19:28
  • 4
    At some point the statement is not well thought. If you can't catch the Stackoverflow you maybe never know WHERE it happened in a production environment. – Offler Jan 21 '15 at 10:45
1

You can check stack before executing method with RuntimeHelpers.EnsureSufficientExecutionStack method msdn.

using System.Runtime.CompilerServices;

class Program
{
   static void Main()
   {
      try
      {
          Recursion();
      }
      catch (InsufficientExecutionStackException e)
      {
      }
   }

   static void Recursion()
   {
       RuntimeHelpers.EnsureSufficientExecutionStack();
       Recursion();
   }
}
Daniil
  • 21
  • 3