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.

Configuration

This detector does not need any configuration.