Exception thrown from a Finally block
ID |
vbnet.correctness.throw_from_finally_block |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
low |
Resource |
Error Handling |
Language |
VB.NET |
Description
Reports a Throw statement inside the Finally clause of a Try statement, whether it is the first statement of the clause or follows the cleanup code, and points the finding at the thrown expression. Throwing from the Try body or from a Catch clause is not reported: those are the normal ways to signal a failure, because the exception can still be caught by an enclosing handler and carries the original failure with it.
Rationale
A Finally clause runs on both paths out of the Try statement, including the path where an exception is already travelling up the stack. Throwing there abandons that exception: the new one takes its place, the original object and the stack trace that lead back to the statement which actually failed are discarded, and no handler ever sees them. The caller is told that cleanup went wrong and is given no way to find out what went wrong first, so the log entry names the symptom of the recovery instead of the fault - the class of failure that can only be diagnosed by reproducing it. On the path where the Try body succeeded, the same Throw turns a completed operation into a failure reported from cleanup, at a point where the caller has every reason to believe the work is done.
The following code illustrates the pattern detected by this rule:
Public Sub Import(ByVal path As String)
Dim reader As StreamReader = Nothing
Try
reader = New StreamReader(path)
Apply(reader.ReadToEnd())
Finally
' FLAGGED: Exception thrown from a Finally block
Throw New InvalidOperationException("import finished in an unknown state")
End Try
End Sub
Remediation
Keep the Finally clause to cleanup that cannot fail - guard the state you rely on rather than asserting it, as in If reader IsNot Nothing Then reader.Dispose(). When a cleanup step can genuinely fail, wrap that step in its own Try … Catch inside the Finally clause and record the failure without propagating it, so the in-flight exception survives; if the caller must learn about it, attach it to the original exception as an inner exception or aggregate the two rather than replacing one with the other. Validation and state checks that were placed in the Finally clause belong in the Try body, or after the whole Try statement, where an enclosing handler can still act on them.