vite lastVerified: 2026-05-08

Vite build error: production build fails after dev server runs fine

Vite build error — production-only failures and rollup misalignment

On this page
  1. What causes this error
  2. How to fix it manually
  3. Copy this prompt into your AI coding agent
  4. Why this prompt works
  5. Variants by symptom
  6. Related errors and tools

What causes this error

Vite uses two different bundlers depending on phase. Dev runs on esbuild — fast, lenient, permissive about ambiguous module shapes. Production runs on Rollup — slower, stricter, unforgiving about anything that smells like a side effect or a circular dependency. The most demoralising production build errors are the ones the dev server hid for weeks because esbuild silently accepted what Rollup will not.

Four divergence patterns explain almost every report of this class. First: dead-code elimination drops side-effect-only imports. A line like import 'reflect-metadata' is a side-effect import — it has no exports the consumer references — so Rollup’s tree-shaker removes it unless the package’s package.json declares "sideEffects" truthfully. esbuild in dev mode preserves it; Rollup in prod drops it; the runtime crashes only after deploy. Second: peer-dependency version skew. The dev tree resolved one version of a peer (because the dev tool pulled it transitively); the production install resolves a different version (because the lockfile was regenerated). Type definitions agree; runtime contracts diverge. Third: dynamic-import chunking under Rollup splits code paths esbuild fused. A React.lazy(() => import('./Heavy')) becomes a separate chunk in production; if ./Heavy had a top-level side effect the eager-imported code path relied on, the chunked version executes that side effect later or not at all. Fourth: CommonJS interop differences — esbuild flattens module.exports = { default: x } to a usable default import; Rollup’s commonjs plugin requires explicit configuration in vite.config.ts optimizeDeps.include or the dynamic shape leaks through.

How to fix it manually

Run pnpm build --debug and read the Rollup warnings before the error — the warnings about circular dependencies and sideEffects field misconfiguration are the diagnostic signal. Run pnpm exec vite build --mode production --logLevel info to expose the chunk graph. If the issue is sideEffects, edit the offending package’s consumer to import the symbol that matters (or, if it is your own package, set "sideEffects": false or list the side-effect files explicitly). For peer-dep skew, run pnpm why <package> and pin the version intentionally in the consumer’s package.json.

Copy this prompt into your AI coding agent

vite-build-error-fix-prompt.md
# Goal
Fix this Vite production build error using the smallest safe change.
Diagnose which of four divergence patterns applies (sideEffects drop /
peer-dep skew / dynamic-import chunking / CommonJS interop) before patching.
Production uses Rollup; dev uses esbuild. The fix lives in the Rollup-side
behaviour, not in the source the dev server already accepts.

# Context

The full build output:

<paste pnpm build output including any Rollup warnings BEFORE the error line>

The exact failure mode: <build error / runtime error after build / chunk-loading error>
The package at issue (if narrowed): <package name>

The pnpm why <package> output:

<paste here>

# In-scope

- vite.config.ts optimizeDeps.include and optimizeDeps.exclude
- vite.config.ts build.rollupOptions
- package.json sideEffects declaration (in your own packages)
- package.json dependency version pin (for peer-dep skew)
- The single failing import or chunk-boundary

# Out-of-scope

- Switching from Vite to Webpack or another bundler
- Disabling tree-shaking globally to mask the problem
- Modifying upstream third-party package source
- Renaming components or restructuring routes

# Verification

Run `pnpm build` and confirm exit code 0.
Run `pnpm preview` and reproduce the originally failing path manually.
Run `pnpm exec vite build --mode production --logLevel info` and grep for
"sideEffects" warnings — there should be zero remaining.

# Output format

1. One sentence: which of the four divergence patterns this is.
2. The diff (only changed config + package.json).
3. The verification output, last 15 lines.

Why this prompt works

The four-divergence framing inoculates against the most common Claude Code response to production build errors, which is “rebuild from scratch with different config”. Naming vite.config.ts optimizeDeps and build.rollupOptions in In-scope and excluding “switching bundlers” in Out-of-scope keeps the fix scoped to the Rollup-side behaviour where the divergence actually lives. The verification step explicitly greps for residual sideEffects warnings because Rollup logs them as warnings rather than errors — the build can succeed with warnings present and silently regress on the next deploy.

Variants by symptom

SymptomDiagnostic command
Build succeeds, runtime error in prod onlyInspect dist/assets/ chunk graph
[plugin:vite:resolve] sideEffects warningEdit consumer’s package.json sideEffects
Peer-dep version skewpnpm why <package> and pin the version
Dynamic-import chunk fails to loadCheck build.rollupOptions.output.manualChunks
CommonJS package not interop’dAdd to optimizeDeps.include

Frequently asked questions

Why does my build fail when the dev server works fine?

Dev runs on esbuild, which is lenient, while production runs on Rollup, which is strict. Rollup drops side-effect-only imports and enforces stricter module interop that esbuild silently accepted.

How do I find which import Rollup dropped?

Run pnpm build --debug and read the Rollup warnings about sideEffects and circular dependencies that print just before the error line.