vercel / vercel/next.js

Turbopack: stale browserslist-rs data causes all SWC compat transforms to be enabled, panicking on ** operator

Open
#92,091 3 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

linear: turbopack Turbopack
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/mhdsdt/turbopack-bigint-exponentiation-repro

To Reproduce
  1. Clone: git clone https://github.com/mhdsdt/turbopack-bigint-exponentiation-repro
  2. npm install --force
  3. npm run dev (runs next dev --turbopack)
  4. Open http://localhost:3000 -- 500 error with Rust panic

The reproduction is a single page with an async function using the ** operator and a standard browserslist (>0.2%, not dead, not op_mini all) in package.json. No external dependencies beyond Next.js and React.

npm run dev:webpack works fine.

Current vs. Expected behavior

Current: Turbopack panics:

thread 'tokio-runtime-worker' panicked at
  swc_ecma_compat_es2015-42.0.0/src/generator.rs:507:21:
  not yet implemented: right-associative binary expression

Expected: The page should compile successfully. The ** operator is standard ES2016, async is standard ES2017, and every browser in the >0.2%, not dead list supports both natively.

Root cause (traced through source code):

There is a caniuse-lite data version mismatch between the JS and Rust sides of the Turbopack pipeline:

  1. Next.js resolves the browserslist on the JS side using an up-to-date caniuse-lite (knows Chrome 146, Firefox 149, etc.), then sends the resolved browser strings to Turbopack (hot-reloader-turbopack.ts:280)
  2. Turbopack re-resolves them using browserslist-rs v0.19.0 (published July 2025, only knows up to ~Chrome 141) with ignore_unknown_versions: true, so unknown versions are silently dropped, returning an empty distribution list
  3. Versions::parse_versions([]) returns all-None fields
  4. is_any_target() returns true (all None = "unknown target")
  5. When is_any_target is true, every SWC compat transform is enabled, including async-to-generator and the ES2015 generator transform
  6. async-to-generator converts the async function into a generator
  7. The generator transform encounters ** and panics at todo!("right-associative binary expression")

Why this only affects Turbopack (not webpack on canary):

SWC PR #11457 (merged Jan 2026) added an unknown_version flag to preset_env: when a browserslist query returns empty results, it assumes a modern browser and skips all transforms. Webpack benefits from this because SWC's targets_to_versions correctly sets unknown_version: true on empty results.

Turbopack bypasses this fix. It pre-resolves the browserslist in Rust (environment.rs:96-97), then passes already-parsed versions via Targets::Versions(versions) (transform/mod.rs:206), which always sets unknown_version: false in targets_to_versions. The unknown_version guard never fires.

Chrome version boundary test:

Browserslist Result Why
No browserslist OK Uses hardcoded defaults (chrome 111, edge 111, firefox 111, safari 16.4)
chrome 141 OK Known to browserslist-rs 0.19.0
chrome 142 Panic Unknown to browserslist-rs, silently dropped, is_any_target = true
firefox 130 OK Known
firefox 148 Panic Unknown
>0.2%, not dead Panic Resolves to Chrome 142+, Firefox 147+, etc.
>1% OK Happens to resolve to only known versions
Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0
  Available memory (MB): 24576
  Available CPU cores: 10
Binaries:
  Node: 20.20.0
  npm: 10.8.2
  Yarn: 1.22.22
  pnpm: 10.24.0
Relevant Packages:
  next: 16.2.1-canary.13
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A
Which area(s) are affected?

Turbopack, SWC

Which stage(s) are affected?

next dev (local), next build (local)

Additional context

This will affect more and more projects over time as browsers release new versions that browserslist-rs 0.19.0 doesn't know about. Any project with a standard browserslist (>0.2% is the Create React App default) will eventually hit this.

In real-world projects, the panic typically manifests through node_modules packages that contain async functions with the ** operator, since Turbopack transpiles node_modules (unlike webpack). Known affected packages include permissionless, @noble/curves, and @walletconnect/utils.

Possible fixes:

  1. Update browserslist-rs in Turbopack to match the JS-side caniuse-lite data
  2. Detect when parse_versions returns all-None and set unknown_version: true in Turbopack's preset-env config, so the existing SWC fix (#11457) applies
  3. Pass the browserslist query string to SWC instead of pre-resolving it in Rust, so Turbopack uses the same code path as webpack

Key source locations:

Related: pimlicolabs/permissionless.js#499 (same error, reported on the wrong repo)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the linked turbopack-bigint-exponentiation-repro and trace the browser target flow through hot-reloader-turbopack.ts:280, turbopack-core/src/environment.rs, and turbopack-ecmascript/src/transform/mod.rs. Compare the handling of empty parsed targets with the SWC unknown_version path, then verify that the reported browserslist compiles the page without a Rust panic in next dev and next build.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, rust
Domain
build-system, compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.