oxc-project/oxc · warning

Too many nested callbacks ({num}). Maximum allowed is {max}.

Error message

Too many nested callbacks ({num}). Maximum allowed is {max}.

What it means

Emitted by the `max-nested-callbacks` rule when function callbacks are nested more deeply than allowed; oxc's default is `DEFAULT_MAX_NESTED_CALLBACKS = 10` (crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs:28). The visitor counts function expressions/arrow functions passed as arguments inside other callbacks, and the help text suggests promises or refactoring. It targets callback-hell maintainability.

Source

Thrown at crates/oxc_linter/src/rules/eslint/max_nested_callbacks.rs:18

use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::Semantic;
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::Value;

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

fn max_nested_callbacks_diagnostic(num: u32, max: u32, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!("Too many nested callbacks ({num}). Maximum allowed is {max}."))
        .with_help("Reduce nesting with promises or refactoring your code.")
        .with_label(span)
}

#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct MaxNestedCallbacks {
    /// The `max` enforces a maximum depth that callbacks can be nested.
    max: u32,
}

const DEFAULT_MAX_NESTED_CALLBACKS: u32 = 10;

impl Default for MaxNestedCallbacks {
    fn default() -> Self {
        Self { max: DEFAULT_MAX_NESTED_CALLBACKS }
    }
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Refactor to async/await or promise chaining so nesting is flattened to sequential statements.
  2. Name and hoist the inner callbacks (`function onUserLoaded(user) {...}`) so they become sibling declarations rather than nested expressions.
  3. Raise `max` in config if the deep nesting is structural and unavoidable (e.g. generated glue code), or disable the rule for those files.

Example fix

// before
getUser(id, (err, user) => {
  getOrders(user, (err, orders) => {
    getInvoices(orders, (err, invoices) => render(invoices));
  });
});

// after
const user = await getUser(id);
const orders = await getOrders(user);
const invoices = await getInvoices(orders);
render(invoices);
Defensive patterns

Strategy: validation

Validate before calling

// .oxlintrc.json -> "max-nested-callbacks": ["error", 3]
// New async code review runs oxlint and fails anything deeper before merge.

Prevention

When it happens

Trigger: Chain 11 or more nested callbacks, typically with default max 10: nested waterfall Node.js style code, deeply chained libraries (`lib.a(x, (r1) => lib.b(r1, (r2) => ...))`), or repeated `array.map(() => array.map(() => ...))` layering beyond 10 levels. Config `{ "max-nested-callbacks": ["error", 3] }` makes it trigger much sooner.

Common situations: Legacy Node.js code from the pre-Promise era; code targeting very old browsers where promises/async-await were unavailable; enabling the rule with a strict max on an existing async codebase; recursively building config objects with nested callbacks.

Related errors


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