{"record":{"id":"398b3c1f1a1c4e9b","repo":"oxc-project/oxc","slug":"unsafe-finally-block","errorCode":null,"errorMessage":"Unsafe `finally` block.","messagePattern":"Unsafe `finally` block\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs","lineNumber":12,"sourceCode":"use oxc_ast::{\n    AstKind,\n    ast::{BreakStatement, ContinueStatement},\n};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::{GetSpan, Span};\n\nuse crate::{AstNode, context::LintContext, rule::Rule};\n\nfn no_unsafe_finally_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unsafe `finally` block.\")\n        .with_help(\n            \"Control flow inside `try` or `catch` blocks will be overwritten by this statement.\",\n        )\n        .with_label(span)\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NoUnsafeFinally;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Disallow control flow statements in `finally` blocks.\n    ///\n    /// ### Why is this bad?\n    ///\n    /// JavaScript suspends the control flow statements of `try` and `catch`\n    /// blocks until the execution of a `finally` block finishes.","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_unsafe_finally.rs#L1-L30","documentation":"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.","triggerScenarios":"try { return 1; } finally { return 2; }; finally { break label; }; finally { continue; }; finally { throw e2; } masking the original error from try or catch.","commonSituations":"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.","solutions":["Move the return/throw out of the finally block into code after the try statement.","Record the outcome in a variable inside try/catch and act on it after the try statement.","Keep finally strictly to release/rollback logic with no branching transfer statements.","For break/continue, hoist the loop decision out or use a flag the loop checks."],"exampleFix":"// before\nfunction f() {\n  try { return compute(); }\n  finally { return fallback; }\n}\n// after\nfunction f() {\n  let result;\n  try { result = compute(); }\n  finally { cleanup(); }\n  return result;\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat finally as cleanup-only: never return, throw, break, or continue inside it.","Communicate outcomes via variables declared before the try and acted on after it.","Keep the rule enabled (correctness category) so CI catches swallowed errors early."],"tags":["eslint","oxlint","try-catch","finally","control-flow"],"backgroundTag":"control-flow-in-finally","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}