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
- Promisify the outer API and use one style end to end: `promisify(doSomething)().then(doSomethingElse).then(render)`
- Commit to callbacks fully inside the callback and convert at the boundary
- Set `{ "exemptDeclarations": true }` if your function declarations are module entry points, not callbacks
- 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
- Convert at boundaries: promisify the callback API once, then stay promise-only inside
- Reserve `(err, value) =>` signatures for pure callback code
- If function declarations are entry points, set `exemptDeclarations: true` deliberately, not silently
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
- Promise executor functions should not be `async`.
- Unexpected `await` inside a loop.
- Function parameter(s) use the `done` argument
- Avoid nesting promises.
- Do not use `new` on `Promise.{static_name}`
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/720fb86ad547f1e3.
Report an issue: GitHub.