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
- 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
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
- 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
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
- Promise should not be resolved multiple times. Promise is po
- Promise executor functions should not be `async`.
- Unexpected `await` inside a loop.
- Avoid nesting promises.
- Do not use `new` on `Promise.{static_name}`
AI-assisted analysis of oxc-project/oxc@36ec0ef2ba (2026-08-20).
Data as JSON: /api/errors/c591eb2f6f7a7f86.
Report an issue: GitHub.