npm run build fails but the dev server works fine
Any AI coding tool Last checked 2026-10-14
The dev server is permissive and lazy; the production build is strict and compiles everything. Code the agent wrote can run fine in dev and still fail type checking, linting, or prerendering at build time. Run the real build locally before you push — that is the only check that matches what your host runs.
What you saw
npm run dev is clean, the app works in the browser, and npm run build fails — type errors, Module not found, window is not defined, or a prerender error on a specific route.
Frequently discovered only when the deploy fails, minutes after you were told everything was working.
What it actually means
Dev and build are different pipelines, and the agent only ever saw the dev one succeed:
- Type checking. Dev servers often transpile without type checking for speed.
tsc runs for real at build time, so type errors surface only then. - Lazy compilation. Dev compiles routes on demand. A route you never visited is compiled for the first time during the build.
- Server vs browser. Prerendering and server rendering execute your code in Node, where
window, document and localStorage do not exist. In dev, the page may only ever have run in the browser. - Case-sensitive paths. macOS and Windows filesystems are usually case-insensitive; the Linux build machine is not.
./components/Button and ./components/button are the same file locally and a Module not found in CI. - Dependency placement. Something the build needs is in
devDependencies, or was installed locally but never added to package.json.
The fix
Reproduce the real build locally. This is the single highest-value habit here:
npm run build
npm run start # or: npx serve dist
Read the first error only. Build failures cascade; fixing error one usually removes the next twenty. Do not paste the whole log at the agent and ask it to fix everything.
For window is not defined, move the access out of module scope and behind an effect or a dynamic import:
if (typeof window !== 'undefined') { /* browser-only */ }
For Module not found that works locally, check the import's capitalisation against the real filename. Verify with git, which records the true case:
git ls-files | grep -i button
Verify a clean install matches your machine — this catches missing or misplaced dependencies:
rm -rf node_modules
npm ci
npm run build
Do not silence the check to make the build pass. Disabling type errors in the build config hides the failure until runtime, where it costs more.
How to stop it happening again
Add the build to your definition of done. Tell the agent in the project rules file: a task is not complete until npm run build passes.
Run type checking in the background during development (tsc --watch --noEmit) so type errors show up when they are written, not at deploy time.
Keep imports consistently cased and prefer a path alias (@/components/Button) over long relative chains.
Build on every push in CI, so the failure arrives attached to the commit that caused it rather than a week later.
Related questions
Should I just turn off type errors during build?
No. It converts a build failure you can see into a runtime failure your users see. If you need to ship urgently, fix the one error — it is almost always a two-line change.
Why does it only break on the host and not on my machine?
Usually filename case sensitivity, a Node version difference, or a dependency that exists in your node_modules but not in package.json. npm ci from a clean checkout reproduces all three.
The agent says it fixed it but the build still fails.
Have it paste the actual command output rather than describing it. Agents routinely report success from reasoning about the change instead of running the build.