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 registeredView on GitHub (pinned to e1e7af627c)
Solutions
- Move every `onMounted`/`onUnmounted`/... call above the first `await` in setup.
- If the hook callback needs awaited data, keep registration synchronous and reference a reactive ref that is filled in later.
- Move the async work into `onMounted` or a `watchEffect` so setup remains synchronous.
- 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
- Register all lifecycle hooks at the top of setup, before any await.
- Keep setup synchronous; move async work into onMounted or watchEffect.
- Enable vue/no-lifecycle-after-await in CI to catch reordering during refactors.
- Be extra careful when adding top-level await (Suspense components) to existing setups.
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
- `{name}` is forbidden after an `await` expression.
- [HIRBuilder] expected block {:?} to exist
- Inline helpers are not supported yet
- TS5042
- TS5081
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/22466e10b2a53bc3.
Report an issue: GitHub.