throw inside a finally block masks the original exception
ID |
dart.correctness.throw_from_finally_block |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
medium |
Remediation Effort |
medium |
Resource |
Error Handling |
Language |
Dart |
Description
Reports throw statements written directly in a finally block, rather than inside a closure the finally block itself contains.
Rationale
A finally block runs while an exception from the try body is still propagating. Throwing from it replaces that exception with the new one, and the original — the failure that actually started the unwinding — is lost together with its stack trace. Callers then diagnose the cleanup error instead of the real fault, and the two failures can look unrelated. The substitution happens silently, so nothing in the report hints that a first exception existed.
The following code illustrates the pattern detected by this rule:
class Session {
void close(String id) {
// FLAGGED: throw inside a finally block masks the original exception
try {
flush(id);
} finally {
if (!released) {
throw StateError('lock still held for $id');
}
}
}
void flush(String id) {}
void release() {}
bool released = false;
}
Remediation
Keep the finally block to cleanup that cannot fail, and report a cleanup problem without unwinding: log it, or record it and surface it after the try statement completes. When the condition really must abort the operation, validate it before the try statement, or raise it from a catch clause where the original exception is available to attach as a cause.