oxc-project/oxc · warning · OxcDiagnostic

Unsafe `finally` block.

Error message

Unsafe `finally` block.

What it means

Diagnostic from oxlint's no-unsafe-finally rule. A break/continue/return/throw inside a finally block overwrites the control flow already in flight from try/catch; for example a return in finally silently swallows an exception thrown in try. The rule flags these control-flow statements when their enclosing block is a finally.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs:12

use oxc_ast::{
    AstKind,
    ast::{BreakStatement, ContinueStatement},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

use crate::{AstNode, context::LintContext, rule::Rule};

fn no_unsafe_finally_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unsafe `finally` block.")
        .with_help(
            "Control flow inside `try` or `catch` blocks will be overwritten by this statement.",
        )
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoUnsafeFinally;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallow control flow statements in `finally` blocks.
    ///
    /// ### Why is this bad?
    ///
    /// JavaScript suspends the control flow statements of `try` and `catch`
    /// blocks until the execution of a `finally` block finishes.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Move the return/throw out of the finally block into code after the try statement.
  2. Record the outcome in a variable inside try/catch and act on it after the try statement.
  3. Keep finally strictly to release/rollback logic with no branching transfer statements.
  4. For break/continue, hoist the loop decision out or use a flag the loop checks.

Example fix

// before
function f() {
  try { return compute(); }
  finally { return fallback; }
}
// after
function f() {
  let result;
  try { result = compute(); }
  finally { cleanup(); }
  return result;
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: try { return 1; } finally { return 2; }; finally { break label; }; finally { continue; }; finally { throw e2; } masking the original error from try or catch.

Common situations: Cleanup helpers that 'return a status' from finally; error masking when cleanup throws; labeled control flow leaking out of finally; copy-pasted cleanup blocks that branch.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/398b3c1f1a1c4e9b. Report an issue: GitHub.