{"record":{"id":"c591eb2f6f7a7f86","repo":"oxc-project/oxc","slug":"promise-should-not-be-resolved-multiple-times-pro","errorCode":null,"errorMessage":"Promise should not be resolved multiple times. Promise is already resolved on line {line}.","messagePattern":"Promise should not be resolved multiple times\\. Promise is already resolved on line (.+?)\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/promise/no_multiple_resolved.rs","lineNumber":26,"sourceCode":"use oxc_cfg::{\n    BlockNodeId, ControlFlowGraph, EdgeType, ErrorEdgeKind, InstructionKind,\n    graph::{\n        Direction,\n        visit::{Control, DfsEvent, EdgeRef, set_depth_first_search},\n    },\n};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_semantic::{Scoping, SymbolId};\nuse oxc_span::Span;\nuse rustc_hash::{FxHashMap, FxHashSet};\n\nuse crate::{\n    AstNode, context::LintContext, rule::Rule, utils::get_promise_constructor_inline_executor,\n};\n\nfn already_resolved_diagnostic(line: usize, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        \"Promise should not be resolved multiple times. Promise is already resolved on line {line}.\"\n    ))\n    .with_label(span)\n}\n\nfn potentially_already_resolved_diagnostic(line: usize, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\"Promise should not be resolved multiple times. Promise is potentially resolved on line {line}.\")).with_label(span)\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NoMultipleResolved;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// This rule warns of paths that resolve multiple times in executor functions of Promise constructors.\n    ///\n    /// ### Why is this bad?","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/oxc-project/oxc/blob/36ec0ef2bae567d1a8413ada2fe795f1af1f2785/crates/oxc_linter/src/rules/promise/no_multiple_resolved.rs#L8-L44","documentation":"Diagnostic from the oxlint rule `promise/no-multiple-resolved` (plugin `promise`, category `suspicious`). It fires when the inline executor of a `new Promise(...)` calls `resolve`/`reject` again on a code path where that promise was certainly already settled. The rule builds a control-flow graph (oxc_cfg) with dominator analysis and tracks calls to the executor's resolve/reject parameters by symbol id, so 'already resolved on line N' means every incoming CFG path had a prior settle call. Extra settle calls are silent no-ops under the Promises/A+ spec, which is why they are reported as suspicious logic errors rather than crashes.","triggerScenarios":"Two or more settle calls in the same basic block (`reject(e); resolve(v)`); a settle call after an `if` block that settles without an early `return`/`else`; a conditional settle inside a loop (while/for/do-while) followed by another settle after the loop; a settle in `try` plus another in `finally`.","commonSituations":"Wrapping Node-style error-first callbacks in a Promise and forgetting the `else` branch or `return` after `reject(error)`; executors written as try/finally; timer/event handlers that can fire more than once; incremental migration of callback code into Promises.","solutions":["Add an early `return` or an `else` branch right after the first `reject(...)`/`resolve(...)` so later settle calls are unreachable","Restructure the executor so exactly one settle runs per path: `if (error) { reject(error) } else { resolve(value) }`","Delete the redundant settle call - the second call has no effect and only obscures intent","Replace the hand-written wrapper with `util.promisify(fn)` or an `async` function, which settle exactly once"],"exampleFix":"// before\nnew Promise((resolve, reject) => {\n  fn((error, value) => {\n    if (error) {\n      reject(error)\n    }\n    resolve(value)\n  })\n})\n\n// after\nnew Promise((resolve, reject) => {\n  fn((error, value) => {\n    if (error) {\n      reject(error)\n    } else {\n      resolve(value)\n    }\n  })\n})","handlingStrategy":"validation","validationCode":"# fail CI before merge when an executor settles twice on one path\nnpx oxlint --promise/no-multiple-resolved src/","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair `if (error) reject(error)` with an `else` or an early `return`","Prefer `util.promisify(fn)` or `async/await` over hand-written executors","Treat extra resolve/reject calls as dead code: delete them instead of relying on the spec ignoring them","Aim for exactly one settle statement per code path in every executor you review"],"tags":["promise","async","lint","control-flow","oxlint"],"backgroundTag":"promise-resolved-multiple-times","analyzedSha":"36ec0ef2bae567d1a8413ada2fe795f1af1f2785","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}