oxc-project/oxc · warning · OxcDiagnostic

Prefer top-level await over using an async IIFE.

Error message

Prefer top-level await over using an async IIFE.

What it means

This is the async-IIFE diagnostic of oxlint's `unicorn/prefer-top-level-await`. It flags the historical pattern `(async () => { ... })()` (or `(async function () { ... })()`) used only to get `await` at module top level, and recommends top-level `await` in ES modules instead, which removes the wrapper and its scoping/error-handling quirks.

Source

Thrown at crates/oxc_linter/src/rules/unicorn/prefer_top_level_await.rs:19

use oxc_ast::{
    AstKind,
    ast::{Expression, VariableDeclarationKind},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

use crate::{
    AstNode, ast_util::is_method_call, ast_util::variable_declaration_kind, context::LintContext,
    rule::Rule,
};

fn prefer_top_level_await_over_promise_chain_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Prefer top-level await over using a promise chain.").with_label(span)
}

fn prefer_top_level_await_over_async_iife_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Prefer top-level await over using an async IIFE.").with_label(span)
}

fn prefer_top_level_await_over_async_function_call_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Prefer top-level await over an async function call.")
        .with_help("Add `await` before the function call.")
        .with_label(span)
}

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

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Prefer top-level await over top-level promises and async function calls.
    ///
    /// ### Why is this bad?
    ///

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Unwrap the IIFE and `await` its body directly at module top level; move the `.catch` into a `try { ... } catch` block or keep a top-level `await promise.catch(...)` if you need to swallow errors.
  2. Confirm the file is ESM and the runtime supports top-level await before applying.
  3. If the IIFE exists to create a scope (name isolation), keep it and disable the rule for the line, or use a block `{ ... }` plus `await`.

Example fix

// before
(async () => {
  await init();
  await migrate();
})();

// after
await init();
await migrate();
Defensive patterns

Strategy: validation

Validate before calling

// In ESM, drop the async IIFE wrapper
await init();
await migrate();
// CI: npx oxlint --deny-warn unicorn/prefer-top-level-await src/

Try / catch

// Replace IIFE .catch with a top-level try/catch
try {
  await init();
} catch (err) {
  console.error('boot failed', err);
  process.exitCode = 1;
}

Prevention

When it happens

Trigger: A top-level expression statement that is an immediately-invoked async arrow or function expression in a module; the rule pairs this with the promise-chain and async-call diagnostics from the same file to cover the three common pre-top-level-await idioms.

Common situations: Legacy bootstrap code like `(async () => { await init(); await migrate(); })().catch(console.error);` written before Node 14.8/bundler TLA support; after migrating the package to ESM these wrappers become dead weight and obscure stack traces.

Related errors


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