oxc-project/oxc · warning · OxcDiagnostic

Avoid using promises inside of callbacks.

Error message

Avoid using promises inside of callbacks.

What it means

Diagnostic from the oxlint rule `promise/no-promise-in-callback` (plugin `promise`, category `suspicious`). It fires when a promise-returning call (per `is_promise`) appears inside an ancestor function identified as an error-first callback - a function-like node whose first parameter is the conventional error parameter. The rule exempts promise calls that are the direct argument of a `return` statement (chain links, per the comment in the source) and promise handlers themselves; the `exemptDeclarations` option (default `false`) additionally skips function declarations. Mixing the two async styles makes error handling inconsistent: callbacks use error-first, promises use `catch`.

Source

Thrown at crates/oxc_linter/src/rules/promise/no_promise_in_callback.rs:16

use oxc_ast::{AstKind, ast::FormalParameters};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

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

fn no_promise_in_callback_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Avoid using promises inside of callbacks.")
        .with_help("Use either promises or callbacks exclusively for handling asynchronous code.")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
struct NoPromiseInCallbackConfig {
    /// Whether or not to exempt function declarations. Defaults to `false`.
    exempt_declarations: bool,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema)]
pub struct NoPromiseInCallback(NoPromiseInCallbackConfig);

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallows the use of Promises within error-first callback functions.

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Promisify the outer API and use one style end to end: `promisify(doSomething)().then(doSomethingElse).then(render)`
  2. Commit to callbacks fully inside the callback and convert at the boundary
  3. Set `{ "exemptDeclarations": true }` if your function declarations are module entry points, not callbacks
  4. Wrap only at the outermost boundary and keep inner code promise-only

Example fix

// before
doSomething((err, data) => {
  if (err) console.error(err)
  else doSomethingElse(data).then(console.log)
})

// after
const { promisify } = require('util')
promisify(doSomething)()
  .then(doSomethingElse)
  .then(console.log)
  .catch(console.error)
Defensive patterns

Strategy: validation

Validate before calling

npx oxlint --promise/no-promise-in-callback src/

Prevention

When it happens

Trigger: `doSomething((err, data) => doSomethingElse(data).then(render))` - the promise call is inside an `(err, ...) =>` callback and not in return position.

Common situations: Layering promise-based APIs on top of callback-based libraries (fs, events, older SDKs) during incremental migration; codebases mid-transition between styles.

Related errors


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