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.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved