oxc-project/oxc · warning · OxcDiagnostic

Top-level await is not available in the configured target…

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.

Solutions

  1. 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
  2. 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)
  3. 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
  4. 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

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


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

Appendix: 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)