oxc-project/oxc · warning · OxcDiagnostic

Promise should not be resolved multiple times. Promise is po

Error message

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

What it means

Diagnostic from the oxlint rule `promise/no-multiple-resolved` (plugin `promise`, category `suspicious`). This is the flow-sensitive variant: the CFG analysis found that at least one (but not all) incoming path to the second `resolve`/`reject` call had already settled the promise, so the promise is 'potentially' already resolved on line N. The 'potentially' wording comes from the ResolvedKind::Potential classification in the rule's dominator-based path analysis. Such calls are no-ops when they are redundant, but indicate a missing guard, `else`, or early `return`.

Source

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

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?
    ///
    /// Multiple resolve/reject calls:
    /// - Violate the Promise/A+ specification
    /// - Have no effect on the Promise's behavior
    /// - Make the code's intent unclear
    /// - May indicate logical errors in the implementation
    ///

View on GitHub (pinned to 36ec0ef2ba)

Solutions

  1. Make the branches exclusive: add `else` before the trailing `resolve(value)` or `return` inside the reject branch
  2. Guard the later settle with the inverse condition: `if (!error) resolve(value)`
  3. Delete whichever settle call is unreachable dead code
  4. Replace the wrapper with `util.promisify` or rewrite as an `async` function so there is exactly one settle

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)
      return
    }

    resolve(value)
  })
})
Defensive patterns

Strategy: validation

Validate before calling

# the same rule covers the 'potentially' variant - run it in pre-commit/CI
npx oxlint --promise/no-multiple-resolved src/

Prevention

When it happens

Trigger: `if (error) { reject(error) }` followed by an unconditional `resolve(value)` (reject branch may or may not run); a settle inside a nested `if` at any depth with a later unconditional settle; `if (foo) { ... if (bar) reject(e) ... } resolve(v)` where the inner reject only fires on some paths.

Common situations: Error-first callback wrappers missing the `else`; guard clauses partially covering paths; refactors that move code below an existing conditional reject; copy-pasted executors where only some branches were updated.

Related errors


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