I have a worker class that catches uncaught exceptions and logs them for tracking purposes. However, I do NOT want to swallow those exceptions, nor do I want to abstract them (i.e. losing the stack trace). I believe the best practice in the industry is to throw a new exception and set the inner exception to the caught exception.

With that in mind, I would like a peer review of my implementation in a stripped down form that can be reproduced in a console application:

private static void ExecuteAction(Action action) {
    Exception uncaughtException = null;
    try {
    } catch (Exception e) {
        Console.WriteLine(e.Message); // In reality this logs to a database.
        uncaughtException = e;

    // Some fake extra work for sample purposes.
    for (int i = 0; i < 1000; i += 3)
        i -= 2;

    if (uncaughtException != null)
        throw new Exception("An uncaught exception was detected by the worker. Check the inner exception for details.", uncaughtException);

To test it out, in your main method of a console application:

try {
    int maxValue = int.MaxValue;
    ExecuteAction(() => {
        Console.WriteLine("This is a test.");
        Console.WriteLine(checked(maxValue + 1));
catch (Exception e) {
    if (e.InnerException != null)
        Console.WriteLine($"Inner Exception: {e.InnerException.Message}");

The expected output is:

This is a test.

Arithmetic operation resulted in an overflow.

An uncaught exception was detected by the worker. Check the inner exception for details.

Inner Exception: Arithmetic operation resulted in an overflow.

Are there some scenarios that may not be covered by this? Are there any gotchas to this implementation?

  • 1
    \$\begingroup\$ Rethrow it throw uncaughtException; instead of new \$\endgroup\$
    – aepot
    Mar 5, 2021 at 0:07
  • 2
    \$\begingroup\$ Why not just use a finally block for code you want executed if throws or not? \$\endgroup\$ Mar 5, 2021 at 1:30
  • \$\begingroup\$ What if your original exception has an inner exception? Your current code doesn't seem to log those inner messages. You'll need to loop through those to get them all, e.g. github.com/BJMdotNET/code-samples/blob/master/… \$\endgroup\$
    – BCdotWEB
    Mar 5, 2021 at 7:46
  • \$\begingroup\$ @aepot you lose stack trace information that way. \$\endgroup\$ Mar 5, 2021 at 13:53
  • \$\begingroup\$ @CharlesNRice because the code after the try-catch-finally needs to execute regardless. The point of the worker is to track unhandled errors (among other things). The finally will only execute if the exception is handled somewhere up the call stack. In my case, the worker lives in a DLL that is referenced by many applications on the same system, so it needs to record the exception, regardless of if someone catches it up stream :) \$\endgroup\$ Mar 5, 2021 at 13:56

1 Answer 1


I think that is where ExceptionDispatchInfo comes into play.

  • It resides inside the System.Runtime.ExceptionServices namespace.

It preserve the original exception and won't truncate/alter the StackTrace.

  • It appends the StackTrace with one more StackFrame from where the exception is re-thrown.
class Program
    static void Main(string[] args)
        ExceptionDispatchInfo edi = null;

            // Do your stuff
            throw new Exception("A");
        catch (Exception ex)
            edi = ExceptionDispatchInfo.Capture(ex);

        //Do whatever is needed, like logging

The output:

Unhandled exception. System.Exception: A
   at EDI_Demo.Program.Main(String[] args) in C:\Users\...\Program.cs:line 15
--- End of stack trace from previous location where exception was thrown ---
   at EDI_Demo.Program.Main(String[] args) in C:\Users\...\Program.cs:line 23
  • Line 15: throw new Exception("A");
  • Line 23: edi?.Throw();
  • 2
    \$\begingroup\$ Ohhhhhh I love that! It's, it's beautiful! Thank you! :D \$\endgroup\$ Mar 5, 2021 at 13:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.