oxc-project/oxc · warning · OxcDiagnostic

Promise should not be resolved multiple times. Promise is al

Error message

Promise should not be resolved multiple times. Promise is already resolved on line {line}.

What it means

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.

Source

Thrown at crates/oxc_linter/src/rules/promise/no_multiple_resolved.rs:26

use oxc_cfg::{
    BlockNodeId, ControlFlowGraph, EdgeType, ErrorEdgeKind, InstructionKind,
    graph::{
        Direction,
        visit::{Control, DfsEvent, EdgeRef, set_depth_first_search},
    },
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{Scoping, SymbolId};
use oxc_span::Span;
use rustc_hash::{FxHashMap, FxHashSet};

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

fn already_resolved_diagnostic(line: usize, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!(
        "Promise should not be resolved multiple times. Promise is already resolved on line {line}."
    ))
    .with_label(span)
}

fn potentially_already_resolved_diagnostic(line: usize, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!("Promise should not be resolved multiple times. Promise is potentially resolved on line {line}.")).with_label(span)
}

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

declare_oxc_lint!(
    /// ### What it does
    ///
    /// This rule warns of paths that resolve multiple times in executor functions of Promise constructors.
    ///
    /// ### Why is this bad?

View on GitHub (pinned to 36ec0ef2ba)

Solutions

  1. Add an early `return` or an `else` branch right after the first `reject(...)`/`resolve(...)` so later settle calls are unreachable
  2. Restructure the executor so exactly one settle runs per path: `if (error) { reject(error) } else { resolve(value) }`
  3. Delete the redundant settle call - the second call has no effect and only obscures intent
  4. Replace the hand-written wrapper with `util.promisify(fn)` or an `async` function, which settle exactly once

Example fix

// before
new Promise((resolve, reject) => {
  fn((error, value) => {
    if (error) {
      reject(error)
    }
    resolve(value)
  })
})

// after
new Promise((resolve, reject) => {
  fn((error, value) => {
    if (error) {
      reject(error)
    } else {
      resolve(value)
    }
  })
})
Defensive patterns

Strategy: validation

Validate before calling

# fail CI before merge when an executor settles twice on one path
npx oxlint --promise/no-multiple-resolved src/

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Related errors


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