On this page

Skip to content

A Brief Discussion on the Differences Between throw and throw ex

TLDR

  • Using throw; preserves the original Stack Trace, ensuring that the original location of the error can be tracked during debugging.
  • Using throw ex; resets the Stack Trace, causing the loss of error stack information; it should be avoided.
  • If you need to throw a new Exception, pass the original Exception as an InnerException to preserve the error context.
  • If a catch block contains only throw; with no other logic, the try...catch block should be removed.
  • .NET 5 and later versions provide the CA2200 rule, which automatically detects and warns against the improper use of throw ex.

Analysis of the Differences Between throw and throw ex

In C# try...catch blocks, there is a significant difference in behavior between throw; and throw ex;.

Using throw to Preserve Stack Information

When does this issue arise: When you need to intercept an exception in a catch block, perform logging or processing, and then re-throw the original exception.

Using throw; re-throws the caught Exception and fully preserves the original Exception stack information. This is crucial for debugging as it provides the correct path where the error occurred.

csharp
try {
    try {
        int result = Divide(1, 0);
    } catch {
        throw;
    }
} catch (Exception e) {
    Console.WriteLine(e.ToString());
}

static int Divide(int numerator, int denominator) {
    return numerator / denominator;
}

The execution results show that the error path points correctly to the Divide method on line 3:

text
System.DivideByZeroException: Attempted to divide by zero.
   at Program.<<Main>$>g__Divide|0_0(Int32 numerator, Int32 denominator) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 12
   at Program.<Main>$(String[] args) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 3

Using throw ex Causes Stack Loss

When does this issue arise: When developers mistakenly believe that throw ex is the standard way to re-throw an exception, ignoring the fact that it treats the exception object as a "new" source of error.

Using throw ex creates and throws a new Exception, which resets the Exception's stack information, making it difficult to track.

csharp
try {
    try {
        int result = Divide(1, 0);
    } catch (Exception ex) {
        throw ex;
    }
} catch (Exception e) {
    Console.WriteLine(e.ToString());
}

The execution results show that the error location has changed to the line number where throw ex is located, and the original error path is lost:

text
System.DivideByZeroException: Attempted to divide by zero.
   at Program.<Main>$(String[] args) in D:\Programming\Projects\TestThrow\TestThrow\Program.cs:line 5

TIP

Test results show that for an Exception re-thrown using throw ex, its InnerException is null, which confirms that the original stack information is indeed lost.

Best Practices and Recommendations

Correct Exception Wrapping

When does this issue arise: When you need to throw a custom Exception while wanting to preserve the original error information.

We should throw a new Exception and pass the original Exception as the innerException parameter. This allows you to add new context information while retaining the original stack trace.

csharp
public class CustomException : Exception {
    public CustomException(string message, Exception innerException)
        : base(message, innerException) {
    }
}

try {
    // Code that might throw an Exception...
} catch (Exception ex) {
    // ex must be passed as an InnerException to preserve information
    throw new CustomException("Additional information", ex);
}

Avoid Redundant try...catch

When does this issue arise: When a try...catch block is written in the code, but the catch block only executes throw; without any additional processing.

If there is no processing logic inside the catch block other than throw;, the try...catch block should be removed directly, as it has no positive impact on program execution.

csharp
// It is recommended to remove such meaningless catch blocks
try {
    // Code that might throw an Exception...
} catch {
    throw;
}

Change Log

  • 2025-07-31 Initial document creation.