How can I throw an exception without losing the original stack trace information in .NET?

Use the "throw" keyword in place of "throw ex" to perserve the original stack trace information.

When you throw an exception using “throw ex” instead of “throw” you override the original stack trace with a new stack trace that starts from the throwing method.  This can make tracking down the root causes of exceptions much more difficult.  Take a look at the example below in order to see the differences in the stack traces depending on which option you use.

class ExceptionallySpeaking
{

    static void Main(string[] args)
    {
        try
        {
            MethodA();
        }
        catch (Exception ex)
        {
            //Option 1 - Showing overridden exception stack with MethodA instead of MethodB on top
            //Exception: System.NotImplementedException: The method or operation is not implemented.
            //   at ExceptionallySpeaking.MethodA()
            //   at ExceptionallySpeaking.Main(String[] args)

            //Option 2 - Showing original exception stack with MethodB on top
            //Exception: System.NotImplementedException: The method or operation is not implemented.
            //   at ExceptionallySpeaking.MethodB()
            //   at ExceptionallySpeaking.MethodA()
            //   at ExceptionallySpeaking.Main(String[] args)

            //Option 3 - Showing original exception stack with MethodB on top plus additional information
            //Exception: System.Exception: Additional information... ---> System.NotImplementedException: The method or operation is not implemented.
            //   at ExceptionallySpeaking.MethodB()
            //   at ExceptionallySpeaking.MethodA()
            //   --- End of inner exception stack trace ---
            //   at ExceptionallySpeaking.MethodA()
            //   at ExceptionallySpeaking.Main(String[] args)

             Console.WriteLine("Exception: {0}", ex.ToString());

        }
    }

    public static void MethodA()
    {
        
        try
        {
            MethodB();
        }
        catch (Exception ex)
        {
            //Option 1 - Overrides original stack trace
            throw ex;

            //Option 2 - Keeps original stack trace
            throw;

            //Option 3 - Keeps original stack trace and adds aditional information
            throw new Exception("Additional information...", ex);

                
        }
    }

    public static void MethodB()
    {
        throw new System.NotImplementedException();
    }

}

 

Let's dig a little deeper on this topic and discover the mystery behind Throw vs. Throw ex and the shrinking stack trace.

In Microsoft intermediate language (MSIL), the "throw ex" in C# will be converted into "throw" while "throw" in C# will be converted into "rethrow".  This difference is what causes "throw ex" to contain less stack trace information since it will restart the exception throwing process from MethodA instead of continuing to rethrow the original exception from MethodB.

Option 1 - MethodA catch block using "throw ex" in MSIL

  catch [mscorlib]System.Exception
  {
    IL_000b:  stloc.0
    IL_000c:  nop
    IL_000d:  ldloc.0
    IL_000e: throw
  }  // end handler

Option 2 - MethodA catch block using "throw" in MSIL

  catch [mscorlib]System.Exception
  {
    IL_000b:  stloc.0
    IL_000c:  nop
    IL_000d: rethrow
  }  // end handler