Rethrowing the caught exception discards its stack trace
ID |
vbnet.correctness.preserve_stack_trace |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Error Handling |
Language |
VB.NET |
Description
Reports Throw ex inside the Catch clause that declared ex, at any nesting depth within the clause. Rethrowing the caught exception variable by name restarts its stack trace at the Throw statement. The correct forms are not reported: a bare Throw, which rethrows without touching the trace, and Throw New …(message, ex), which wraps the original as the inner exception.
Rationale
The stack trace on a .NET exception is filled in as the exception propagates, and it is reset when the exception object is thrown again. Throw ex therefore replaces the frames that lead back to the statement that actually failed with a single frame pointing at the Catch clause. The report or log entry then names the handler instead of the fault: the deep call chain through the data access, parsing or network code where the error originated is gone, and the only way to find it again is to reproduce the failure. This is one of the most expensive defects to live with, because it removes the information precisely in the situations where it is needed most - rare and intermittent production failures.
The following code illustrates the pattern detected by this rule:
Public Sub Submit(ByVal orderId As Integer)
Try
Using command As New SqlCommand("UPDATE orders SET state = 'SUBMITTED' WHERE id = @id", _connection)
command.Parameters.AddWithValue("@id", orderId)
command.ExecuteNonQuery()
End Using
Catch ex As SqlException
Console.Error.WriteLine("submit failed for order " & orderId.ToString())
' FLAGGED: Rethrowing the caught exception discards its stack trace
Throw ex
End Try
End Sub
Remediation
Use a bare Throw when the intent is to rethrow the exception unchanged after logging or cleaning up - it preserves the original stack trace and the original throw site. When the exception should be reported as a different, more meaningful type, construct the new exception with the caught one as the inner exception, Throw New OrderException(message, ex), so the original trace remains reachable through InnerException. Reserve Throw ex for the case where ex is an exception the current method created and has not thrown yet.