oxc-project/oxc · warning · OxcDiagnostic
Top-level `await` prevents this module from being loaded wit
Error message
Top-level `await` prevents this module from being loaded with `require(esm)`.
What it means
Diagnostic from oxlint rule node/no-top-level-await (restriction). Node.js v20.19 introduced require(esm), but an ES module containing top-level await cannot be loaded that way — require() throws ERR_REQUIRE_ASYNC_MODULE. The rule flags `await` expressions, `for await...of` loops, and `await using` declarations at module top level (any depth NOT nested in a function) so published packages stay loadable from both CommonJS and ESM consumers. The diagnostic's own note says it targets published packages; private apps may disable it.
Source
Thrown at crates/oxc_linter/src/rules/node/no_top_level_await.rs:15
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
AstNode,
context::LintContext,
rule::{DefaultRuleConfig, Rule},
};
fn no_top_level_await_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Top-level `await` prevents this module from being loaded with `require(esm)`.")
.with_help("Move the `await` inside an `async` function, as ES modules with top-level `await` cannot be loaded with `require(esm)`.")
.with_note("This rule is intended for published packages. Consider disabling it if this package is private.")
.with_label(span)
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
struct NoTopLevelAwaitConfig {
/// If `true`, top-level `await` is allowed in files that start with a
/// hashbang (`#!`), which marks them as executable scripts rather than
/// importable modules.
ignore_bin: bool,
}
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct NoTopLevelAwait(NoTopLevelAwaitConfig);
declare_oxc_lint!(View on GitHub (pinned to e1e7af627c)
Solutions
- Move the awaited work into an exported async function the caller invokes: export async function init() { ... }
- Initialize lazily: export a promise or an init() rather than awaiting at module load
- For executable scripts, keep the hashbang and configure { "ignoreBin": true }
- For private apps never loaded via require(), disable the rule in .oxlintrc.json as the diagnostic note suggests
Example fix
// before (top level of module)
const foo = await import('foo');
// after
export async function load() {
const foo = await import('foo');
return foo;
} Defensive patterns
Strategy: validation
Validate before calling
// .oxlintrc.json
"rules": { "node/no-top-level-await": ["error", { "ignoreBin": true }] }
// pre-publish gate for dual-format packages
npx oxlint -c .oxlintrc.json --deny-warning . && npm publish --dry-run Prevention
- Design package entry points as synchronous module definitions plus exported async init() functions
- If your repo mixes apps and published packages, scope this rule to the package directories via overrides
- In CI, additionally smoke-require built ESM output from a CJS harness to catch ERR_REQUIRE_ASYNC_MODULE before release
When it happens
Trigger: run() reports when a node is an AwaitExpression, a ForOfStatement with r#await == true, or a VariableDeclaration of await-using kind, AND no ancestor is a Function or ArrowFunctionExpression. Suppressed only when the ignoreBin option is true and the file starts with '#!' (an executable script, not an importable module). Triggers: 'const foo = await import("foo");' or a top-level 'for await (const e of gen())'.
Common situations: Dual-format (CJS+ESM) libraries preparing for require(esm) on Node 20.19+/22; top-level await used for config/db initialization during ESM migration; executable scripts with a hashbang that legitimately top-level-await (use ignoreBin); private apps where the constraint is irrelevant.
Related errors
- Expected {name} instead of {actual}
- This module could be mistakenly parsed as script instead of
- Unexpected access to `exports`.
- Unexpected access to `module.exports`.
- Unexpected assignment to `exports`.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/cde7328c9552322b.
Report an issue: GitHub.