oxc-project/oxc · warning · OxcDiagnostic

Expected return with your callback function.

Error message

Expected return with your callback function.

What it means

Diagnostic from oxlint rule node/callback-return (style category, ported from ESLint n/callback-return). In Node callback-style code, after you invoke the completion callback the function must stop: if (err) { callback(err); } followed by more statements runs that extra code and can end up calling the callback twice. The rule detects callbacks purely by callee name — defaults are callback, cb, next, configurable as an array that also accepts dotted paths like obj.method — and demands the call be part of a return statement (or immediately followed by a bare return).

Source

Thrown at crates/oxc_linter/src/rules/node/callback_return.rs:20

use serde::Deserialize;

use oxc_ast::{
    AstKind,
    ast::{CallExpression, Expression, Statement},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use oxc_str::CompactStr;

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

fn callback_return_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Expected return with your callback function.")
        .with_help("Return the callback call or add an explicit return immediately after it.")
        .with_label(span)
}

#[derive(Debug, Clone, JsonSchema, Deserialize)]
/// The rule takes a single option - an array of possible callback names - which may include object methods. The default callback names are `callback`, `cb`, `next`.
pub struct CallbackReturn(Box<CallbackNames>);

#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(transparent)]
struct CallbackNames(Vec<CompactStr>);

impl Default for CallbackReturn {
    fn default() -> Self {
        Self(Box::new(CallbackNames(vec!["callback".into(), "cb".into(), "next".into()])))
    }
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Prefix the call with return: 'return callback(err);' — the canonical fix
  2. If the call must not be the return value, add an explicit 'return;' as the very next statement so nothing after it executes
  3. If the callback genuinely fires exactly once and the warning is a known if/else false positive, rename the parameter so it stops matching callback/cb/next, or disable the rule for the file
  4. Configure the rule with your real callback names: "node/callback-return": ["error", ["callback", "cb", "next", "obj.method"]]
  5. Long term, refactor the function to async/await or Promises so the callback disappears entirely

Example fix

// before
function done(err) {
  if (err) {
    callback(err);
  }
  callback(); // runs even after the error callback
}

// after
function done(err) {
  if (err) {
    return callback(err);
  }
  callback();
}
Defensive patterns

Strategy: validation

Validate before calling

// .oxlintrc.json
"rules": {
  "node/callback-return": ["error", ["callback", "cb", "next", "done"]]
}

// CI gate
npx oxlint -c .oxlintrc.json --deny-warning .

Prevention

When it happens

Trigger: A CallExpression whose callee is a plain identifier or member chain whose source text exactly equals a configured callback name, nested inside a function, where the closest block ancestor is not a ReturnStatement or concise arrow body, and the callback statement is neither the last statement of the function body nor the statement immediately preceding a final 'return'. Canonical trigger: function a(err) { if (err) { callback(err); } horse(); } — the call sits mid-block with code after it.

Common situations: Callback-era Express/MongoDB codebases migrated from ESLint's n/callback-return to oxlint; enabling the whole node preset in a shared config; documented false positive: if/else branches that each call the callback once still warn (static analysis cannot prove single-invocation); documented false negatives: setTimeout(callback, 0), IIFE-wrapped or process.nextTick-nested calls are not matched at all.

Related errors


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