oxc-project/oxc · warning · OxcDiagnostic
Top-level await is not available in the configured target en
Error message
Top-level await is not available in the configured target environment.
What it means
Oxc's ES2022 transform emits this warning when it finds an `await` expression at module top level while the configured target environments do not support top-level await. The `top_level_await` flag is derived from a browserslist/engine target query (crates/oxc_transformer/src/options/env.rs:160) via `has_feature(ES2022TopLevelAwait)`, which returns true when at least one target engine lacks the feature and it therefore needs lowering. Oxc has no transform that lowers top-level await to older syntax, so it warns instead of silently emitting code that will fail at runtime in the target.
Source
Thrown at crates/oxc_transformer/src/es2022/mod.rs:133
fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(class_properties) = &mut self.class_properties {
class_properties.enter_static_block(block, ctx);
}
}
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(class_properties) = &mut self.class_properties {
class_properties.exit_static_block(block, ctx);
}
}
fn enter_await_expression(
&mut self,
node: &mut AwaitExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.top_level_await && Self::is_top_level(ctx) {
let warning = OxcDiagnostic::warn(
"Top-level await is not available in the configured target environment.",
)
.with_label(node.span);
ctx.state.error(warning);
}
}
}
impl ES2022<'_> {
fn is_top_level(ctx: &TraverseCtx) -> bool {
ctx.current_hoist_scope_id() == ctx.scoping().root_scope_id()
}
}
View on GitHub (pinned to e1e7af627c)
Solutions
- Raise the target query so every engine supports top-level await (Chrome >= 89, Firefox >= 89, Safari >= 15, Node >= 14.8), e.g. `EnvOptions::from_target_list(&["chrome 89","node 14.8"])` or update .browserslistrc, so `has_feature(ES2022TopLevelAwait)` returns false
- Remove the top-level await: move the await into an async function and call it (fire-and-forget or via an init function the entry point awaits)
- If the warning is noise because a downstream bundler (esbuild/webpack) handles TLA itself, configure oxc to preserve the syntax (empty/modern targets) and let the bundler lower it
- Wrap the async work in a dynamic-import chain: move the awaiting module behind `await import("./module.mjs")` executed from an async entry
Example fix
// before (targets = chrome 60, module top level)
const config = await loadConfig();
export default config;
// after (no TLA)
async function init() {
const config = await loadConfig();
return config;
}
export default init(); // Promise; consumers do `await init()` or (await import(...)) Defensive patterns
Strategy: validation
Validate before calling
// Before transforming, check whether any target lacks top-level await
use oxc_transformer::EnvOptions;
let env = EnvOptions::from_target_list(&["chrome 89", "firefox 89", "safari 15", "node 14.8"])?;
// If you cannot raise targets, scan the source for top-level await first:
fn uses_top_level_await(src: &str) -> bool {
// cheap heuristic: parse with oxc_parser and walk Program-level
// statements for ExpressionStatement(AwaitExpression) or `for await`
oxc_parser::parse(src).program_has_top_level_await()
} Type guard
// TypeScript consumers: narrow the transform setup at compile time
type Targets = string[];
const TLA_SAFE: Targets = ["chrome>=89", "firefox>=89", "safari>=15", "node>=14.8"];
function assertTlaSafe(targets: Targets): void {
// fail the build config early instead of per-file diagnostics
if (!TLA_SAFE.every(t => targets.some(x => x.startsWith(t.split(">")[0])))) {
throw new Error('targets include engines without top-level await');
}
} Try / catch
// Diagnostics are collected, not thrown: after running the transformer,
// inspect the returned diagnostics and fail or gate on severity
let result = transformer.build(source);
for d in &result.errors {
if d.message.contains("Top-level await is not available") {
// route to a legacy no-TLA build path or fail the target
}
} Prevention
- Pin browserslist/engine targets to TLA-capable versions (chrome>=89, firefox>=89, safari>=15, node>=14.8) in the same PR that introduces top-level await
- Keep an explicit allowlist in CI: grep new code for module-level `await ` when the legacy target matrix is still active
- Treat this warning as a runtime breaker, not lint noise: the emitted code throws SyntaxError in engines without TLA
- Document one async-init pattern (exported promise or `await import`) for the repo so contributors avoid ad-hoc top-level awaits
When it happens
Trigger: Running the oxc transformer with a `targets`/browserslist query that includes engines without top-level await (e.g. `chrome >= 60`, older node, legacy safari), causing `ES2022Options::top_level_await` to be set, and then transforming a module whose top-level hoist scope contains an `AwaitExpression` (e.g. `const data = await fetch(url);` at module top level). The check is `enter_await_expression` in crates/oxc_transformer/src/es2022/mod.rs:127 — it fires only when `options.top_level_await && current_hoist_scope_id == root_scope_id`, so awaits inside functions are unaffected.
Common situations: Modernizing a codebase that uses top-level await for config/env loading while CI still pins `targets` to browserslist `defaults` or an old `.browserslistrc`; switching a bundler pipeline from esbuild/swc to oxc with copied-over legacy target lists; Node CLIs targeting Node < 14.8; shared library code consumed by both modern and legacy bundles. The internal comment at env.rs:76 notes that enabling this flag errors for ALL top-level awaits, so any single TLA usage trips it.
Related errors
- Arbitrary module namespace identifier names are not availabl
- Big integer literals are not available in the configured tar
- Namespace tags are not supported by default. React's JSX doe
- Please provide an explicit key value. Using \"key\" as a sho
- TS1202
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/3c882c3d1ab61ea1.
Report an issue: GitHub.