oxc-project/oxc · error · OxcDiagnostic

Lifecycle hook `{hook_name}` is called after `await` in `set

Error message

Lifecycle hook `{hook_name}` is called after `await` in `setup()`.

What it means

The vue/no-lifecycle-after-await rule fires when a Vue lifecycle hook (`onMounted`, `onUnmounted`, ...) is registered after an `await` inside `setup()` / `<script setup>`. Lifecycle registration is synchronous: once the first `await` yields, the component may already be mounted, so a hook registered afterwards will never be invoked. The rule resolves imported hook names through the module record (ImportEntry/ImportImportName) so aliased and re-exported hook imports are matched too.

Source

Thrown at crates/oxc_linter/src/rules/vue/no_lifecycle_after_await.rs:21

    ast::{
        AwaitExpression, CallExpression, ExportDefaultDeclarationKind, Expression, Function,
        ObjectExpression,
    },
};
use oxc_ast_visit::{VisitJs, walk_js};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{ScopeFlags, Scoping, SymbolId};
use oxc_span::Span;
use rustc_hash::FxHashMap;

use crate::module_record::{ImportEntry, ImportImportName};
use crate::{
    AstNode, context::LintContext, frameworks::FrameworkOptions, rule::Rule, utils::find_property,
};

fn no_lifecycle_after_await_diagnostic(span: Span, hook_name: &str) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!(
        "Lifecycle hook `{hook_name}` is called after `await` in `setup()`."
    ))
    .with_help("Lifecycle hooks should be called synchronously in `setup()`. Move the hook call before the first `await`.")
    .with_label(span)
}

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

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallow asynchronously registered lifecycle hooks.
    ///
    /// ### Why is this bad?
    ///
    /// Lifecycle hooks must be registered synchronously during `setup()` execution.
    /// If a lifecycle hook is called after an `await` statement, it may be registered

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Move every `onMounted`/`onUnmounted`/... call above the first `await` in setup.
  2. If the hook callback needs awaited data, keep registration synchronous and reference a reactive ref that is filled in later.
  3. Move the async work into `onMounted` or a `watchEffect` so setup remains synchronous.
  4. Re-run oxlint to confirm hook calls precede all awaits.

Example fix

// before
<script setup>
const user = await getUser();
onMounted(() => console.log(user.name)); // called after `await`
</script>

// after
<script setup>
const user = ref(null);
onMounted(async () => {
  user.value = await getUser();
  console.log(user.value.name);
});
</script>
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: `await fetch(...); onMounted(() => {...});` in `<script setup>`, or the equivalent in a `setup()` function. Any awaited promise followed by an `on*` hook call within the same async setup scope.

Common situations: Components that fetch data at the top of setup before subscribing to lifecycle events; refactors that introduce top-level await (Suspense-based components); copy-pasting hook registration below newly added async initialization.

Related errors


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