Next.js 16 Turbopack webpack Config Error: Don't Silence It
Next.js 16 fails the build when it finds a webpack config, and the error suggests turbopack: {}. I tested it. The build passes — and the service worker silently disappears.
If next build just failed with this, you're on Next.js 16 and something in your config is using webpack:
⨯ ERROR: This build is using Turbopack, with a `webpack` config and no `turbopack` config.
This may be a mistake.
As of Next.js 16 Turbopack is enabled by default and
custom webpack configurations may need to be migrated to Turbopack.
NOTE: your `webpack` config may have been added by a configuration plugin.
...
TIP: Many applications work fine under Turbopack with no configuration,
if that is the case for you, you can silence this error by passing the
`--turbopack` or `--webpack` flag explicitly or simply setting an
empty turbopack config in your Next config file (e.g. `turbopack: {}`).The quickest fix is the one the error hands you: add turbopack: {}. Before you do, read what it actually turns off.
I reproduced this on this site, which runs Next.js 16.1.1 with @serwist/next 9.5.12 for offline support. With turbopack: {} added, the build went green — every page generated, exit code 0. And public/sw.js, the service worker, was never written. The site would have deployed looking perfect, with its offline mode quietly gone.
Why you're seeing it when you never wrote a webpack config
Next.js 16 made Turbopack the default for both next dev and next build. The upgrade guide is explicit about what happens next:
If your project has a custom
webpackconfiguration and you runnext build(which now uses Turbopack by default), the build will fail to prevent misconfiguration issues.
Most people hitting this never wrote a webpack function. A plugin wrapped their config and added one. The error even says so — "your webpack config may have been added by a configuration plugin" — and so does the upgrade guide.
withSerwistInit from @serwist/next is one. Payload CMS's withPayload is another, tracked in its own issue. Any with…() wrapper around your Next config is a suspect.
That's the important part. The failure isn't Next.js being fussy. It's Next.js telling you: a plugin wants to do work in webpack, and Turbopack is about to skip that work.
What each escape hatch actually does
The error and the docs offer three ways out. They are not equivalent.
| Fix | Bundler used | Your webpack config |
|---|---|---|
turbopack: {} in next.config | Turbopack | Ignored, silently |
next build --turbopack | Turbopack | Ignored, silently |
next build --webpack | webpack | Runs as before |
The upgrade guide describes the --turbopack option in plain words: "build using Turbopack and ignore your webpack config." That's accurate. The problem is that "ignore" doesn't produce an error, a failed step or a missing page. It produces a smaller build.
The test: green build, no service worker
Here's what I ran, on the same codebase, same commit.
With turbopack: {} added to next.config.ts:
$ npx next build
▲ Next.js 16.1.1 (Turbopack)
✓ Compiled successfully
✓ Generating static pages using 7 workers (127/127)
$ ls public/sw.js
ls: cannot access 'public/sw.js': No such file or directoryWith next build --turbopack and no config change: identical result. Exit code 0, all 127 pages, no sw.js.
With next build --webpack: public/sw.js is generated as normal — about 45 KB.
There was exactly one clue in the Turbopack runs, printed at the very top of the log:
[@serwist/next] WARNING: You are using '@serwist/next' with `next dev --turbopack`,
but it doesn't support Turbopack.Read that again: it says next dev, during a production build. In a CI log that scrolls past in a second, a warning about the dev server is exactly the kind of line you learn to ignore. The warning also tells you how to suppress it with an environment variable — which would remove the only signal left.
Why nothing breaks in the browser either
You might expect the missing file to surface as an error once deployed. It usually won't.
The standard registration pattern — and the one this site uses — catches failures so a service worker problem never breaks the page:
navigator.serviceWorker.register("/sw.js", { scope: "/" }).catch(() => {
// Offline support is a progressive enhancement; never break the page for it.
});That's the right call for users. It also means a 404 on /sw.js produces no visible error at all. The page loads, the app works online, and the only symptom is that offline doesn't — which nobody tests on a normal deploy.
It gets worse for returning visitors. When a browser's periodic update check for an existing service worker gets a 404, the update fails and the previously installed worker stays in place. Users who installed your app before the change keep running the old worker and its old precache. New visitors get none. Two populations, and no error in either.
Why Turbopack can't just run the plugin
Webpack configuration is JavaScript that runs inside webpack — plugins hook into the compilation and emit extra files. Service worker generation is exactly that: Serwist compiles sw.ts, injects the precache manifest, and writes sw.js.
Turbopack is a different bundler written in Rust. It supports a subset of webpack loaders through turbopack.rules, but not webpack plugins. Even for loaders, the Next.js docs list emitFile — the API for writing extra output files — as "No support".
So there's no config you can add to make @serwist/next work under Turbopack. The plugin's work needs a different mechanism entirely.
Fix 1: build production with webpack (what I did)
This is the smallest change and the one the upgrade guide documents directly:
{
"scripts": {
"dev": "next dev",
"build": "next build --webpack",
"start": "next start"
}
}Turbopack stays on for development, webpack runs the production build, and the plugin works exactly as it did on Next.js 15.
The dev side is fine because Serwist is normally disabled outside production anyway:
const withSerwist = withSerwistInit({
swSrc: "src/app/sw.ts",
swDest: "public/sw.js",
disable: process.env.NODE_ENV !== "production",
});This site passes --webpack to next dev as well, for parity between dev and production. You lose Turbopack's dev-server speed, and whether that trade is worth it depends on how big your app is.
The cost of Fix 1 is that you're opting out of the default. The docs say plainly: "We recommend using Turbopack for development and production." Treat --webpack as a stable place to stand while you plan Fix 2, not as a permanent answer.
Fix 2: migrate to @serwist/turbopack
Serwist's maintainers resolved the Turbopack request with a separate package rather than changing @serwist/next. It doesn't hook into the bundler at all — it compiles the service worker with esbuild through a Next.js route handler.
The shape of the migration, from the Serwist docs:
npm i -D @serwist/turbopack esbuild serwist- Wrap your config with
withSerwistfrom@serwist/turbopack. - Add a route handler at
app/serwist/[path]/route.tsusingcreateSerwistRoute(), pointingswSrcat your worker. - In
sw.ts, import from@serwist/turbopack/workerinstead of@serwist/next/worker. - Register with
SerwistProviderfrom@serwist/turbopack/react, usingswUrl="/serwist/sw.js".
Watch the URL change. The worker moves from /sw.js to /serwist/sw.js. Anyone who installed the old worker keeps it until you deal with it — the update-404 behaviour above applies here too. Either keep serving something at the old URL, or have the new registration code unregister the old worker explicitly. And if you have a proxy (the Next.js 16 name for middleware) that excludes /sw.js, update it to skip /serwist/ instead.
Fix 3, whichever you pick: make the build fail loudly
The real lesson isn't about Serwist. It's that a green build proved nothing, because the failure mode was "less output", and nothing was checking the output.
A one-line guard closes that:
{
"scripts": {
"build": "next build --webpack",
"postbuild": "node -e \"require('fs').statSync('public/sw.js')\""
}
}statSync throws if the file is missing, which fails the step and fails the deploy. It takes a second to run.
The same idea generalises to any plugin you're unsure about. Run the build both ways and compare what comes out:
npx next build --webpack && find public .next/static -type f | sort > /tmp/webpack.txt
npx next build --turbopack && find public .next/static -type f | sort > /tmp/turbo.txt
diff <(sed 's/[a-f0-9]\{8,\}//g' /tmp/webpack.txt) <(sed 's/[a-f0-9]\{8,\}//g' /tmp/turbo.txt)Hashed chunk names will differ between bundlers, which is why the hashes are stripped. What you're looking for is a file that exists on one side only — like public/sw.js.
The short version
The error is Next.js doing you a favour. It noticed that a plugin wanted to do work that Turbopack won't do, and it stopped rather than guess. The suggested one-line fix removes that protection, and for any plugin that generates files, the result is a build that succeeds with something missing.
Choose --webpack if you need the plugin working today. Migrate to the plugin's Turbopack-native version when you have time. And either way, check for the output, not just the exit code.
Key takeaways
- Next.js 16 builds with Turbopack by default and fails the build when a webpack config exists — usually one injected by a plugin you installed, not one you wrote.
- turbopack: {} and next build --turbopack both make Turbopack ignore the webpack config silently. With @serwist/next, the build passes and public/sw.js is never generated.
- The only signal was a Serwist warning that mentions next dev during a production build — easy to miss, and suppressible.
- next build --webpack keeps plugins working; @serwist/turbopack is the Turbopack-native replacement, but it moves the worker from /sw.js to /serwist/sw.js.
- Add a postbuild check that the files you depend on exist. A green build only proves the build finished, not that it produced everything.
Frequently asked questions
What does 'This build is using Turbopack, with a webpack config and no turbopack config' mean?
Next.js 16 builds with Turbopack by default. When it finds a webpack function in your Next config — usually added by a plugin such as @serwist/next rather than written by you — it stops the build, because Turbopack does not run webpack configuration and whatever that config does would otherwise be skipped.
Is it safe to add turbopack: {} to silence the error?
Only if you have confirmed the webpack config does nothing you need. An empty turbopack config makes Next.js build with Turbopack and ignore the webpack config entirely. With @serwist/next, the build succeeds and the service worker file is simply never generated. Compare the build output with and without the change before shipping it.
Does Serwist work with Turbopack in Next.js 16?
@serwist/next does not — it generates the service worker inside a webpack plugin, so it needs next build --webpack. Serwist ships a separate package, @serwist/turbopack, that builds the worker through a Next.js route handler with esbuild instead, and serves it from /serwist/sw.js rather than /sw.js.
How do I keep Turbopack for next dev but webpack for production builds?
Pass the flag per script: keep "dev": "next dev" and set "build": "next build --webpack". The Next.js 16 upgrade guide documents exactly this split. If the plugin is disabled outside production, as Serwist's is by default, development loses nothing.
This came out of the same Next.js codebase as my indexing case study. If you're planning a Next.js 16 upgrade and want it done without silent regressions, see my work.
References
- How to upgrade to version 16 — Next.js docsnextjs.org · accessed 2026-09-25
- next.config.js: turbopack — Next.js docsnextjs.org · accessed 2026-09-25
- Turbopack — Serwist docsserwist.pages.dev · accessed 2026-09-25
- [Feature request]: next dev --turbo support (serwist/serwist #54)github.com · accessed 2026-09-25
- withPayload unconditionally injects webpack config (payloadcms/payload #14354)github.com · accessed 2026-09-25
- The service worker lifecycle — web.devweb.dev · accessed 2026-09-25
Last reviewed September 25, 2026
Tahir Nazir
Senior AI Engineer & Full-Stack Lead
5+ years shipping AI-powered products — RAG pipelines, agentic workflows, and MCP tooling. Top Rated on Upwork with a 100% job success score.
More about Tahir →Keep reading
New posts land here first. Follow along by RSS, or get in touch if you are building something similar.
Related articles
Next.js Site Not Indexed? What Took Mine From 21 to 54 Pages
Google had found 22 of my pages and refused to crawl them. Six Next.js mistakes caused it — including one that made every date in my sitemap a lie.TroubleshootingWeb Development12 min readClaude Code on Vertex AI: Why You Get 404s, Wrong Regions and an Opus-Sized Bill
Model not found on Google Cloud's Agent Platform is rarely one problem. A malformed region is silently ignored and falls back to us-east5, ANTHROPIC_VERTEX_PROJECT_ID overrides the project in your credentials, and a deployment with no pinned model is billed at the Opus rate.TroubleshootingAI Engineering10 min readClaude Code Behind a Corporate Proxy: Why NODE_EXTRA_CA_CERTS Isn't the Fix
Unable to get local issuer certificate behind Zscaler or any TLS-inspecting proxy. Claude Code already trusts your OS certificate store by default — so the usual advice fixes it for some people and not others. The variable that actually decides is CLAUDE_CODE_CERT_STORE, and whether your runtime can read the OS store at all.TroubleshootingAI Engineering11 min read