Exceptions That Can't Be Caught by Try-Catch Block in Application Code

Exceptions that can't be caught by try-catch block in application code

Yes, there are some others:

  • The ThreadAbortedException is special. It will always be re-raised when caught unless the catch block calls ResetAbort(). It is entirely uncatchable when the CLR performs a rude abort of the thread. Done when the AppDomain gets unloaded for example, typically at program exit.

  • Any native exceptions thrown by unmanaged code in a thread that got started by native code are uncatchable. The common scenario here is COM components that start their own threads. The CLR is powerless to trap such exceptions, it doesn't know about the thread and can't inject a catch block. If the native code doesn't catch the exception then Windows terminates the process.

  • Any exceptions thrown by finalizers, unless they are critical finalizers. They'll abort the finalizer thread which terminates the process.

  • Starting with .NET 4.0, an ExecutionEngineException is uncatchable. It is thrown by the CLR when it detects that its internal data structures are compromised. Most typically by an AccessViolationException that's raised while the garbage collector is busy. Continuing to execute managed code when the GC heap is compromised is a risky proposition, and exploitable, .NET 4 pulled the plug on it entirely.

  • Starting with the .NET 4.0 version of the CLR, but possibly also present in unmanaged code that you interop with in earlier versions, Microsoft's secure CRT can terminate a program instantly when a security problem is detected. This is not actually an exception under the hood, the process is instantly terminated since the code considers the process compromised and not capable of safely processing exceptions. A common case is where the stack frame of native function is smashed, a common problem in native code and used by viral code to tinker with the return address to run arbitrary code. An attack scenario called "stack buffer overflow". There were a few false alarms in CLR code, early after the .NET 4.0 release but I haven't seen any in quite a while. You can trigger such an abort yourself by writing beyond the bounds of a stackalloc.

  • Quite infamously, exceptions thrown by Windows message handlers when you run code in 32-bit mode in the WOW64 emulation layer on a 64-bit operating system and you have a debugger attached. Best known for the troublesome Load event in Winforms but also present for other messages and in other runtime environments. The ugly details are in this answer.

  • Starting with .NET 4.5, exceptions that Microsoft classifies as Corrupted State Exceptions (CSEs). They can be caught, but that should only ever be done by a top-level exception handler that doesn't do anything but generate a diagnostic for the user's benefit and terminates the app unconditionally. Backgrounder is available in this magazine article.

  • Any exception that is thrown by the jitter before your code can start running cannot be caught or reported. Failure to compile your Main() method is the common case, typically a FileNotFoundException.

Unable to get the exceptions in try catch

That's the expected behavior.

Your catch block in the Program.cs file will only handle exceptions thrown when building and starting the .NET core generic host. That's all. The catch block is not expected to handle exceptions thrown by the registered hosted services during their execution.

If you check the source code for the BackgroundService base class you will understand why exceptions thrown by a background service are lost.

At this line you see that your implementation of ExecuteAsync is invoked and the
resulting task is stored inside the _executeTask field.

Your override of ExecuteAsync is an async function. When there is an unhandled exception inside of an async funcition, the exception is captured by the runtime and placed on the returned Task. The exception will be thrown (with its original stacktrace preserved) when the Task is awaited, or an api like Task.Wait() is called.

Notice that the _executeTask Task is not awaited and that the executing thread is not trying to synchronously wait for the task completion by calling the Task.Wait() method. So, there is no chance to observe exceptions coming from that Task.

There is only one possibility to observe an exception coming from your implementation of ExecuteAsync. If an exception occurs before the very first await instruction, then the method execution completes synchronously and the returned Task will have the IsCompleted property set to true and its Status will be Faulted. In that case, the _executeTask task will be returned to the calling code (see this line).

The calling code, which is basically the code starting the .NET core generic host, will get back a faulted task and will detect the error occurred while starting the background service by awaiting the task itself. At that point, the exception coming from the ExecuteAsync method will be rethrown and the catch block in your Program.cs will handle the exception.

If any exception occurs inside of ExecuteAsync after the first await instruction, there is no way for the .NET core generic host to observe the exception itself. Those exceptions will result in silent failures. You can find more details on this by reading this great blog post.

As pointed out by a comment to your question, this behavior has been changed in .NET 6, as you can read here. To put it briefly, the default behaviour in .NET 6 is logging any unhandled exception coming from a background service and stopping the Host, so that you are aware of the problem and you have a chance to fix your code and investigate why it is throwing exceptions.

Exceptions not catching using catch block. Why?

In order to do a proper try/catch, you need to do the following:

  1. Place your method in the try block
  2. catch the Exception(s)
  3. throw the Exception

Here is some code

try
{
app.MoveFolder(input1, input2);
}
// catch ArgumentException
catch(ArgumentException ex)
{
throw;
}
// catch all others
catch(Exception ex)
{
throw;
}

Why is .NET exception not caught by try/catch block?

I believe I understand the problem. The exception is being caught, the issue is confusion over the debugger's behavior and differences in the debugger settings among each person trying to repro it.

In the 3rd case from your repro I believe you are getting the following message: "NoViableAltException was unhandled by user code" and a callstack that looks like this:


[External Code]
> TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C#
[External Code]
TestAntlr-3.1.exe!TimeDefParser.prog() Line 141 + 0x14 bytes C#
TestAntlr-3.1.exe!TestAntlr_3._1.Program.ParseTest(string Text = "foobar;") Line 49 + 0x9 bytes C#
TestAntlr-3.1.exe!TestAntlr_3._1.Program.Main(string[] args = {string[0x00000000]}) Line 30 + 0xb bytes C#
[External Code]

If you right click in the callstack window and run turn on show external code you see this:


Antlr3.Runtime.dll!Antlr.Runtime.DFA.NoViableAlt(int s = 0x00000000, Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x80 bytes
Antlr3.Runtime.dll!Antlr.Runtime.DFA.Predict(Antlr.Runtime.IIntStream input = {Antlr.Runtime.ANTLRStringStream}) + 0x21e bytes
> TestAntlr-3.1.exe!TimeDefLexer.mTokens() Line 852 + 0xe bytes C#
Antlr3.Runtime.dll!Antlr.Runtime.Lexer.NextToken() + 0xc4 bytes
Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.FillBuffer() + 0x147 bytes
Antlr3.Runtime.dll!Antlr.Runtime.CommonTokenStream.LT(int k = 0x00000001) + 0x2d bytes
TestAntlr-3.1.exe!TimeDefParser.prog() Line 141 + 0x14 bytes C#
TestAntlr-3.1.exe!TestAntlr_3._1.Program.ParseTest(string Text = "foobar;") Line 49 + 0x9 bytes C#
TestAntlr-3.1.exe!TestAntlr_3._1.Program.Main(string[] args = {string[0x00000000]}) Line 30 + 0xb bytes C#
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x39 bytes
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2b bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x3b bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x81 bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x40 bytes

The debugger's message is telling you that an exception originating outside your code (from NoViableAlt) is going through code you own in TestAntlr-3.1.exe!TimeDefLexer.mTokens() without being handled.

The wording is confusing, but it does not mean the exception is uncaught. The debugger is letting you know that code you own mTokens()" needs to be robust against this exception being thrown through it.

Things to play with to see how this looks for those who didn't repro the problem:

  • Go to Tools/Options/Debugging and
    turn off "Enable Just My code
    (Managed only)". or option.
  • Go to Debugger/Exceptions and turn off "User-unhandled" for
    Common-Language Runtime Exceptions.

List of exceptions that CAN'T be caught in .NET

The only exception that cannot be caught directly is (a framework thrown) StackOverflowException. This makes sense, logically, as you don't have the space in the stack to handle the exception at that point. From the docs:

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.

ThreadAbortException can be caught, but will always get re-raised, so has unique behavior. From the docs:

ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.

Also note that some AccessViolationException instances are corrupted state exceptions, and may not get handled by default. These can be handled, but require extra handling via attributes. For details, see Handling Corrupted State Exceptions.

Catch block is not being evaluated when exceptions are thrown from finallys

As it turns out, I am not crazy. Based on the answers I got to this question, I think it seemed like I was having difficulty understanding what is so clearly outlined in the spec. It's really not at all difficult to grasp.

The truth is that the spec makes sense, while the behavior wasn't. This is seen even more so when you run the code in an older runtime, where it behaves completely different...or at least appears to.

A quick recap

What I saw, on my x64 Win7 machine:

  • .NET v2.0-3.5 - WER dialog when the CryptographicException is thrown. After hitting Close the program, the program continues, as if the execption were never thrown. The application is not terminated. This is the behavior one would expect from reading the spec, and is well defined by the architects who implemented exception handling in .NET.

  • .NET v4.0-4.5 - No WER dialog is displayed. Instead, a window appears asking if you want to debug the program. Clicking no causes the program to terminate immediately. No finally blocks are executed after that.

As it turns out, pretty much anybody who would try to answer my question would get the exact same results as I did, so that explains why nobody could answer my question of why the runtime was terminating from an exception that it swallowed.

It's never quite what you expect

Who would have suspected the Just-In-Time debugger?

You may have noticed that running the application under .NET 2 produces a different error dialog than .NET 4. However, if you're like me, you've come to expect that window during the development cycle, and so you didn't think anything of it.

The vsjitdebugger executable was forcibly terminating the application, instead of letting it continue. In the 2.0 runtime, dw20.exe doesn't have this behavior, in fact, the first thing you see is that WER message.

Thanks to the jit debugger terminating the application, it made it seem like it wasn't conforming to what spec says when, in fact, it does.

To test this, I disabled the vsjitdebugger from launching on failure, by changing the registry key at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Auto from 1 to 0. Sure enough, the application ignored the exception and continued on, just like .NET 2.0.

Running in .NET 4.0


As it turns out, there is a workaround, though there's really no reason to workaround this behavior, since your application is terminating.

  1. When the Just-In-Time debugger window pops up, check Manually choose the debugging engines and click yes, that you want to debug.
  2. When Visual Studio gives you engine options, click cancel.
  3. This will cause the program to continue, or a WER dialog to pop up, depending on your machine configuration. If that happens, telling it to close the program won't actually close it, it will continue running as if everything was okay.


Related Topics



Leave a reply



Submit