How Do I Debug Webpack and Vite Build Failures?
How do I debug Webpack and Vite build failures?
TL;DR
- Bottom line: Most Webpack and Vite build failures fall into 5 categories: module not found (wrong path or missing install), loader/plugin misconfiguration (wrong version or missing config), Node.js polyfill removal (Webpack 5 dropped automatic polyfills), ESM/CJS mismatch (wrong module format), and platform-specific binary issues (Rollup native binaries in Vite). [src1, src2, src4]
- Key tool/command:
npx webpack --stats=verbose(Webpack) orvite build --debug(Vite) -- verbose output reveals the exact file, loader, or plugin causing the failure. [src3, src4] - Watch out for: Webpack 5 silently removes Node.js core module polyfills
(
Buffer,process,crypto,path). Code that worked in Webpack 4 will fail at runtime without explicit polyfill configuration. [src2] - Works with: Webpack 4+/5+ (Node.js 10.13+; 5.106 current as of 2026-04, Webpack 6 still in preparation), Vite 4-6 (Node.js 18+) and Vite 7+/8+ (Node.js 20.19+ or 22.12+; Vite 8 defaults to Rolldown bundler). [src2, src1, src8, src9]
Constraints
- Never blindly delete
node_modulesand reinstall without first reading the error message -- this wastes time and may mask the real issue. [src4, src6] - Webpack 5 requires Node.js >= 10.13.0; Vite 6 requires Node.js >= 18; Vite 7+ requires Node.js >= 20.19 or >= 22.12 (Node 18 EOL'd). Always verify the runtime version before debugging build errors. [src2, src1, src8]
- Do not mix ESM and CJS config formats without explicit handling. Webpack uses
webpack.config.js(CJS by default); Vite usesvite.config.js(ESM by default). Mismatched"type": "module"inpackage.jsoncauses silent failures. [src1, src2] - Never add Node.js polyfills to a browser bundle without understanding the bundle size impact. Each
polyfill (
crypto-browserify,buffer,stream-browserify) adds 50-200 KB. Prefer browser-native APIs instead. [src2] - Platform-specific native binaries (Rollup/esbuild in Vite) must match the deployment OS. Cross-platform lockfiles generated on macOS will fail on Linux CI. [src1, src7]
Quick Reference
| # | Error Pattern | Bundler | Likelihood | Signature | Fix |
|---|---|---|---|---|---|
| 1 | Module not found (import path) | Both | ~30% | Module not found: Can't resolve './Foo' |
Fix import path casing; verify file exists [src4, src6] |
| 2 | Module not found (package) | Both | ~20% | Cannot find module 'lodash' |
npm install lodash or check package.json [src6]
|
| 3 | Node.js polyfill missing | Webpack 5 | ~15% | Module not found: Can't resolve 'crypto' |
Add resolve.fallback in webpack config [src2] |
| 4 | Loader not found / misconfigured | Webpack | ~10% | Module build failed: Unknown word |
Install loader: npm i -D babel-loader; check module.rules [src4] |
| 5 | ESM/CJS mismatch | Both | ~8% | ERR_REQUIRE_ESM |
Add "type": "module" to package.json or rename config to
.mjs [src1, src2] |
| 6 | Platform-specific binary missing | Vite | ~5% | Cannot find module '@rollup/rollup-linux-x64-musl' (Vite ≤ 7) or
@rolldown/binding-linux-x64-gnu (Vite 8+) |
Delete lockfile + node_modules, reinstall on target platform [src1, src7,
src8]
|
| 7 | JSON named import (Webpack 5) | Webpack 5 | ~3% | export 'version' was not found in './package.json' |
Use default import: import pkg from './pkg.json' [src2] |
| 8 | Strict mode violation | Vite | ~3% | SyntaxError: With statements cannot be used in strict mode |
Patch dependency via pnpm patch or patch-package [src1] |
| 9 | Chunk load failure (deploy) | Vite | ~3% | Failed to fetch dynamically imported module |
Add vite:preloadError handler; keep old chunks during rollover [src1, src5] |
| 10 | Out of memory | Both | ~3% | FATAL ERROR: Allocation failed - JavaScript heap out of memory |
NODE_OPTIONS=--max-old-space-size=8192 [src3] |
Decision Tree
START — Build fails
├── Error contains "Module not found" or "Can't resolve"?
│ ├── Points to a local file (./src/foo)?
│ │ └── Check file exists, verify casing (Linux is case-sensitive) [src4, src6]
│ ├── Points to a package?
│ │ ├── In node_modules? → npm install / check package.json [src6]
│ │ ├── Node.js core module (crypto, path, fs)?
│ │ │ ├── Webpack 5 → Add resolve.fallback config [src2]
│ │ │ └── Vite → Module externalized; avoid in browser code [src1]
│ │ └── Platform binary (@rollup/rollup-*)?
│ │ └── Delete lockfile + node_modules; reinstall on target platform [src7]
│
├── Error contains "Module build failed" or "Module parse failed"?
│ ├── "Unknown word" → Missing loader for file type [src4]
│ ├── Loader wrong version → npm update loader [src4]
│ └── Syntax error → Check Babel/TS config [src3]
│
├── Error contains "ERR_REQUIRE_ESM"?
│ ├── Config file → Rename to .mjs/.mts or add "type": "module" [src1]
│ └── Dependency → Check exports field in dependency package.json [src2]
│
├── Build succeeds but runtime error?
│ ├── "process is not defined" → Add DefinePlugin for process.env [src2]
│ ├── "Buffer is not defined" → Install buffer polyfill [src2]
│ └── 404 on chunks → Check publicPath / base config [src2, src5]
│
├── Build hangs or OOM?
│ ├── Increase memory: NODE_OPTIONS=--max-old-space-size=8192 [src3]
│ ├── Webpack → cache.type = 'filesystem' [src3]
│ └── Reduce scope: check resolve.modules, loader include paths [src3]
│
└── DEFAULT → Enable verbose output:
├── Webpack: npx webpack --stats=verbose [src3, src4]
└── Vite: npx vite build --debug [src1]
Step-by-Step Guide
1. Read the full error message and enable verbose output
Most build errors include a clear description. Enable verbose/debug output for complete context. [src3, src4]
# Webpack — enable detailed error info
npx webpack --stats=verbose
# Vite — enable debug logging
npx vite build --debug
# Trace Node.js deprecation warnings
node --trace-deprecation node_modules/.bin/webpack --mode production
Verify: Error output shows file paths, loader chain, and resolution attempts.
2. Check Node.js and bundler versions
Version mismatches are a common hidden cause. [src2, src1]
node --version # Webpack 5 >= 10.13.0; Vite 6 >= 18
npx webpack --version
npx vite --version
npm outdated # check for outdated dependencies
Verify: Versions match the minimum requirements for your config.
3. Validate module resolution
For "Module not found" errors, trace how the bundler resolves imports. [src4, src6]
# Check if module is installed
npm list module-name
# Verify file exists with correct casing (critical on Linux)
ls -la src/components/MyComponent.js
# Clear caches
rm -rf node_modules/.cache node_modules/.vite
Verify: npm list module-name shows the package at the expected version.
4. Fix Webpack 5 Node.js polyfill errors
When migrating from Webpack 4, add explicit polyfill configuration. [src2]
// webpack.config.js — Webpack 5 polyfill fallbacks
module.exports = {
resolve: {
fallback: {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"buffer": require.resolve("buffer/"),
"fs": false, // set to false if not needed in browser
"net": false,
"tls": false,
}
},
plugins: [
new (require('webpack')).ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
}),
]
};
Verify: npx webpack completes without "Can't resolve 'crypto'" errors.
5. Fix Vite ESM and platform issues
Resolve common Vite-specific build failures. [src1, src7]
// vite.config.js — common fixes
import { defineConfig } from 'vite';
export default defineConfig({
build: {
target: 'es2015',
chunkSizeWarningLimit: 1000,
},
optimizeDeps: {
force: true, // re-optimize after linking
include: ['problematic-cjs-package'],
},
});
# Force re-optimize dependencies
npx vite --force
# Fix platform-specific binary issues (CI/Docker)
rm -rf node_modules package-lock.json && npm install
Verify: npx vite build completes without errors.
6. Fix loader and plugin configuration (Webpack)
Validate the loader config structure. [src3, src4]
// webpack.config.js — correct loader configuration
const path = require('path');
module.exports = {
module: {
rules: [{
test: /\.jsx?$/,
include: path.resolve(__dirname, 'src'), // CRITICAL: scope loaders
use: {
loader: 'babel-loader', // use full name, NOT 'babel'
options: { cacheDirectory: true },
},
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader'], // order: right-to-left
}, {
test: /\.(png|jpg|gif|svg)$/,
type: 'asset/resource', // Webpack 5 Asset Modules (replaces file-loader)
}],
},
cache: { type: 'filesystem' }, // persistent cache
};
Verify: npx webpack --stats=errors-only returns zero errors.
Code Examples
JavaScript: Webpack 5 config with common error prevention
// Input: webpack.config.js — production-ready config
// Output: Bundle with polyfill fallbacks, optimized loaders, filesystem cache
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js', // contenthash, NOT hash
clean: true,
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
fallback: { "fs": false, "path": false, "crypto": false },
},
module: {
rules: [{
test: /\.[jt]sx?$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader',
options: { cacheDirectory: true },
}],
},
cache: { type: 'filesystem' },
stats: { errorDetails: true },
};
JavaScript: Vite config with common error prevention
// Input: vite.config.js — production-ready config
// Output: Optimized build with CJS compatibility and error handling
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
target: 'es2015',
sourcemap: true,
rollupOptions: {
output: {
chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]',
},
},
},
optimizeDeps: { include: ['axios', 'lodash'] },
server: { watch: { usePolling: true } }, // fix WSL2
});
Bash: Automated build failure diagnostic script
#!/bin/bash
# Input: Run in project root with package.json
# Output: Diagnostic report for Webpack/Vite build failures
echo "=== Build Failure Diagnostic ==="
echo "Node: $(node --version) | npm: $(npm --version)"
# Detect bundler
[ -f "webpack.config.js" ] && echo "Webpack: $(npx webpack --version 2>/dev/null)"
[ -f "vite.config.js" ] && echo "Vite: $(npx vite --version 2>/dev/null)"
# Check module type
TYPE=$(node -e "try{console.log(require('./package.json').type||'commonjs')}catch(e){console.log('missing')}")
echo "Module type: $TYPE"
# Check node_modules health
if [ -d "node_modules" ]; then
echo "node_modules: $(du -sh node_modules 2>/dev/null | cut -f1)"
[ -d "node_modules/.cache" ] && echo "Cache: $(du -sh node_modules/.cache 2>/dev/null | cut -f1)"
else
echo "WARNING: node_modules missing"
fi
echo "--- Outdated packages ---"
npm outdated 2>/dev/null | head -11
Anti-Patterns
Wrong: Adding all Node.js polyfills in Webpack 5
// BAD — polyfilling everything adds 500KB+ to your bundle [src2]
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
module.exports = {
plugins: [new NodePolyfillPlugin()],
};
Correct: Only polyfill what you actually need
// GOOD — explicit fallbacks, unused modules set to false [src2]
module.exports = {
resolve: {
fallback: {
"crypto": false,
"stream": require.resolve("stream-browserify"),
"fs": false,
}
}
};
Wrong: Not scoping loaders with include
// BAD — processes ALL .js files including node_modules [src3]
module.exports = {
module: {
rules: [{ test: /\.js$/, loader: 'babel-loader' }],
},
};
Correct: Scope loaders to source directory
// GOOD — only transpile your own code [src3]
const path = require('path');
module.exports = {
module: {
rules: [{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader',
options: { cacheDirectory: true },
}],
},
};
Wrong: Using require() for ESM-only packages in Vite
// BAD — ESM-only packages cannot be require()'d [src1]
const somePlugin = require('some-esm-only-plugin');
// Error: ERR_REQUIRE_ESM
Correct: Use ES module imports
// GOOD — use ESM imports (rename to .mjs or set "type": "module") [src1]
import somePlugin from 'some-esm-only-plugin';
import { defineConfig } from 'vite';
export default defineConfig({ plugins: [somePlugin()] });
Wrong: Ignoring case sensitivity in imports
// BAD — works on macOS/Windows, fails on Linux CI [src1, src6]
import MyComponent from './components/mycomponent'; // file is MyComponent.js
Correct: Match exact file and folder casing
// GOOD — consistent casing matches filesystem [src1, src6]
import MyComponent from './components/MyComponent';
Common Pitfalls
- Webpack 5
[hash]deprecation: Use[contenthash]instead -- produces stable hashes that only change when content changes. Fix: Replace[hash]with[contenthash]in all output configs. [src2] - Vite
npm linkbreaks pre-bundling: Linked packages are not detected by Vite's optimizer. Fix: Runvite --forceor prefer npm overrides / pnpm workspaces. [src1] - Webpack
Module parse failedon CSS: Webpack does not natively understand CSS. Fix:npm i -D css-loader style-loaderand add the loader rule. [src4] - Vite chunk load failure after deployment: Old HTML references stale chunk hashes. Fix:
Add
vite:preloadErrorlistener to trigger page reload; keep old files during rollover. [src1, src5] - SplitChunks
vendorsrenamed: Webpack 5 renamed todefaultVendors. Old configs silently create unintended chunk groups. Fix: RenamecacheGroups.vendorstocacheGroups.defaultVendors. [src2] - Cross-platform Rollup binary mismatch: Lockfiles from macOS include darwin binaries
that fail on Linux CI. Fix: Regenerate lockfile on target platform or delete before
npm installin Docker. [src7] - Webpack cache invalidation: Filesystem cache may serve stale transforms after updating
dependencies. Fix: Add
"postinstall": "rm -rf node_modules/.cache"to package.json. [src3] - Vite 8 CJS default-import interop change: Vite 8 unified dev and build behavior for
default imports from CJS modules; some packages now return the full
module.exportsobject instead ofmodule.exports.default. Symptom:TypeError: SomeImport is not a functionafter upgrading. Fix: Update the import to use the named export, or temporarily setlegacy.inconsistentCjsInterop: trueinvite.config.jswhile you migrate. [src8] - Vite 8 plugins built for Rollup 4 may not load: Vite 8 uses Rolldown + Oxc instead of
Rollup + esbuild, and removed several plugin hooks. Symptom:
[plugin xxx] hook 'X' is not supported. Fix: Upgrade to a Rolldown-compatible plugin version, or pin Vite to 7.x until the plugin is updated. [src8]
Diagnostic Commands
# === Webpack Diagnostics ===
npx webpack --stats=verbose # verbose build output
npx webpack --stats=errors-only # only errors
node --trace-deprecation node_modules/.bin/webpack --mode production
npx webpack --version # check version
npx webpack --profile --json > stats.json # bundle analysis
node --inspect-brk node_modules/.bin/webpack # Chrome DevTools debug
NODE_OPTIONS=--max-old-space-size=8192 npx webpack # increase memory
# === Vite Diagnostics ===
npx vite build --debug # debug build output
npx vite --force # force re-optimize dependencies
npx vite build --profile # performance profile
npx vite --version # check version
# === General ===
node --version # check Node.js version
npm list package-name # verify installation
npm outdated # list outdated packages
rm -rf node_modules/.cache node_modules/.vite # clear caches
rm -rf node_modules package-lock.json && npm install # full reinstall
Version History & Compatibility
| Version | Status | Key Changes | Migration Notes |
|---|---|---|---|
| Webpack 5.106 | Current (2026-04) | Native CSS (experimental.css) maturing; tsconfig.json paths in
5.105; universal target in progress; Webpack 6 in preparation |
No upgrade required from earlier 5.x; track 2026 roadmap for breaking changes coming with 6.0 [src9] |
| Webpack 5.x | Stable since 2020-10 | Removed Node.js polyfills; Asset Modules; persistent filesystem cache; library.type replaces libraryTarget | Run npx codemod@latest webpack/v5/migration-recipe; add
resolve.fallback [src2] |
| Webpack 4.x | Maintenance | Last version with automatic Node.js polyfills | Upgrade to 5.x; update all loaders/plugins first [src2] |
| Vite 8.x | Current (since 2025-12) | Rolldown bundler now default (replaces Rollup); Oxc replaces esbuild for JS transforms;
Lightning CSS default for minification; CJS default-import interop changed;
'system' and 'amd' output formats removed |
Test bundle size (Lightning CSS may slightly increase output); audit plugins for removed
Rollup hooks; use legacy.inconsistentCjsInterop: true to restore old CJS interop
temporarily [src8] |
| Vite 7.x | Previous stable (2025-06) | Optional Rolldown via rolldown-vite package; Node.js 20.19+ / 22.12+ baseline;
Node 18 dropped |
Upgrade Node first, then npm install vite@7; Rolldown still opt-in [src8] |
| Vite 6.x | Maintenance (2024-11) | Environment API; Rolldown experimental | Check plugin compatibility with the Environment API [src1] |
| Vite 5.x | EOL | Rollup 4; Node.js 18+ required | Upgrade to 6.x → 7.x → 8.x in sequence [src1] |
| Vite 4.x | EOL | SWC support; improved ESM handling | Straightforward upgrade to 5.x [src1] |
When to Use / When Not to Use
| Use This Guide When | Don't Use When | Use Instead |
|---|---|---|
npm run build or vite build fails with errors |
Dev server works but HMR is slow | Vite HMR troubleshooting or server.watch config |
| Migrating from Webpack 4 to 5 | Migrating from Webpack to Vite entirely | Vite migration guide |
| "Module not found" errors during build | Package install fails (npm install errors) |
npm/yarn/pnpm dependency resolution guides |
Runtime process is not defined after Webpack 5 upgrade |
TypeScript type errors (not build errors) | TypeScript compilation error guides |
| CI build fails but local build works | Bundle too large (no errors, just size) | Webpack Bundle Analyzer or vite-plugin-inspect |
Important Caveats
- Webpack and Vite use fundamentally different architectures: Webpack bundles everything through a loader pipeline; Vite serves native ESM in dev and uses Rollup/Rolldown for production. A solution for one may not apply to the other. [src1, src2]
- Vite dev and production builds can behave differently: Vite uses esbuild for dev
dependency pre-bundling but Rollup for production. Code that works in
vite devmay fail invite build. [src1, src5] - Webpack 5
resolve.fallbackonly applies to browser targets: If target is Node.js (target: 'node'), webpack resolves built-in modules natively. [src2] node_modules/.cachecan mask config changes: Both Webpack filesystem cache and Vite pre-bundling cache may serve stale results. Clear caches when debugging unexpected behavior. [src3, src1]- Error messages may point to the wrong file: Due to source maps and module
concatenation, the cited file may be a dependency. Check the full stack trace for the first frame in your
src/directory. [src4]