Minimal reproduction for a Turbopack bug in Next.js 16.2.x where WASM files get a 404 on Vercel deployments due to double-encoding of the deployment ID query parameter.
When Turbopack processes import * as wasm from "./foo.wasm", the WASM chunk
path goes through two URL construction steps that both append the ?dpl=xxx
deployment suffix:
-
.q()(module registration) appends the suffix to the stored path:"static/chunks/xxx.wasm"→"static/chunks/xxx.wasm?dpl=xxx" -
N()(URL construction inloadWebAssembly) splits the path by/,encodeURIComponent's each segment, then appends the suffix again:"static/chunks/xxx.wasm%3Fdpl%3Dxxx"→"/_next/static/chunks/xxx.wasm%3Fdpl%3Dxxx?dpl=xxx"
The %3Fdpl%3Dxxx in the path causes a 404 because no file exists at that path.
- Broken: Next.js 16.2.0, 16.2.1, 16.2.2
- Works: Next.js 16.1.7
In 16.1.7, the equivalent function .v() stored paths without the suffix,
so N() correctly appended it only once.
pnpm install
pnpm build
pnpm start
# Visit http://localhost:3000 — WASM loads successfully- Deploy this repo to Vercel
- Visit the preview deployment URL
- Open browser DevTools → Network tab
- Observe: a
.wasmrequest with%3Fdpl%3Din the path returns 404 - The page shows "WASM loading failed"
From the built output (turbopack-*.js runtime):
// 16.2.2: .q() appends suffix to stored path
S.q = function(e, t) { m.call(this, `${e}${r}`, t) } // r = "?dpl=xxx"
// Both versions: N() also appends suffix
function N(e) {
return `${t}${e.split("/").map(e => encodeURIComponent(e)).join("/")}${r}`
}
// loadWebAssembly calls N() on the already-suffixed path
async loadWebAssembly(e, t, r, n, o) {
let i = fetch(N(r)); // r already has ?dpl=xxx from .q()
// ...
}