oxc-project/oxc · error · OxcDiagnostic

Either await the Promise or pass a callback function to `nex

Error message

Either await the Promise or pass a callback function to `nextTick`.

What it means

Diagnostic from the oxlint rule `vue/valid-next_tick`. In `check_call`, after ruling out zero args and >1 args, if the single-argument call is ALSO 'awaited' per `is_awaited_promise` (parent is await/return/declarator/assignment/expression-arrow, a `.then` member, or inside `Promise.*` array), the rule reports both-consumed usage. Combining a callback with await (or with `.then`, or with `Promise.all`) is redundant: the callback fires on the same tick resolution AND the promise resolution path also runs, which is confusing and often double-handles the update.

Source

Thrown at crates/oxc_linter/src/rules/vue/valid_next_tick.rs:30

    rule::Rule,
    utils::{is_in_vue_component_instance_method, is_this_object, is_vue_next_tick_import},
};

fn should_be_function_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("`nextTick` is a function.").with_label(span)
}

fn missing_callback_or_await_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Await the Promise returned by `nextTick` or pass a callback function.")
        .with_label(span)
}

fn too_many_parameters_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("`nextTick` expects zero or one parameters.").with_label(span)
}

fn either_await_or_callback_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Either await the Promise or pass a callback function to `nextTick`.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct ValidNextTick;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Enforce valid `nextTick` function calls.
    ///
    /// ### Why is this bad?
    ///
    /// `nextTick` is a function that takes either a callback or returns a Promise.
    /// Misuse (accessing it as a value, passing extra arguments, both awaiting and
    /// passing a callback) is almost always a bug.
    ///
    /// ### Examples

View on GitHub (pinned to e1e7af627c)

Solutions

  1. If you migrated to await style, drop the callback: `await nt()`.
  2. If the callback is essential, remove the await: `nt(callback)`.
  3. For `.then` chains, remove the callback argument: `nt().then(anotherCallback)`.

Example fix

// before
async mounted() {
  await this.$nextTick(callback);
}

// after
async mounted() {
  await this.$nextTick();
}
Defensive patterns

Strategy: validation

Validate before calling

if (/await\s+(?:this\.\$nextTick|Vue\.nextTick|\bnt)\s*\(\s*\S/.test(src) ||
    /(?:this\.\$nextTick|Vue\.nextTick|\bnt)\s*\(\s*\S[^)]*\)\s*\.then/.test(src)) {
  console.warn('nextTick used with both a callback and await/.then — pick one');
}

Prevention

When it happens

Trigger: `await nt(callback)`, `await Vue.nextTick(callback)`, `await this.$nextTick(callback)`, and `nt(callback).then(anotherCallback)` (the `.then` parent marks the call awaited) inside a component instance method. One argument plus any await-style parent triggers it.

Common situations: Half-finished migrations from callback style to await style that leave the callback in place; defensive coding that both awaits and passes a callback 'to be sure'; chaining `.then` after a callback form.

Related errors


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