Build errors

Status: đŸŸ© COMPLETE Last updated: 2026-06-21 Plain-English tagline: “It worked yesterday, why won’t it build now?” The Next.js / TypeScript / Vercel build errors that actually come up, and their fixes.


What this is

A paste-and-find reference for build-time errors. Pattern: error message → what it means → fix.

Most of these come from npm run build (locally) or Vercel’s CI (after git push). The ones marked Vercel-only show up in deploy logs but not in next dev.


”Module not found: Can’t resolve ’
’”

What it means: An import statement points at a path that doesn’t exist (from the bundler’s perspective).

Most common causes:

  1. Case sensitivity. You wrote import X from "./button" but the file is Button.tsx. Windows/macOS don’t care; Linux (and Vercel) do.

    • Fix: Match the case exactly. Renaming on a case-insensitive filesystem may need git mv -f Button.tsx button.tsx.
  2. Wrong relative path. ../components/X when it should be ../../components/X.

    • Fix: Use the @/ alias if your tsconfig.json has one configured. @/components/X works from anywhere.
  3. Missing npm install. A teammate added a dep; you pulled but didn’t re-install.

    • Fix: npm install. If still broken: nuke node_modules and re-install.
  4. TypeScript path alias not configured. You wrote import X from "@/lib/X" but tsconfig.json doesn’t define @/*.

    • Fix: Add "paths": { "@/*": ["./*"] } under compilerOptions.
  5. File extension missing or wrong in modern ESM. Some configs require .js extensions in imports.

    • Fix: Add the extension, or set "moduleResolution": "bundler" in tsconfig.json.

Reference: Files and folders — case sensitivity, TypeScript


”Type error: 
”

What it means: TypeScript caught a type mismatch. npm run dev may have skipped this; npm run build won’t.

Common patterns:

  • “Property ‘X’ does not exist on type ‘Y’” — you’re accessing a property TypeScript doesn’t think is there. Either fix the type or use optional chaining (obj?.X).
  • “Argument of type ‘X’ is not assignable to parameter of type ‘Y’” — you passed the wrong type to a function. Either narrow the value (if (typeof x === "string")) or fix the call site.
  • “Object is possibly ‘undefined’” — strict mode forbids using a possibly-undefined value. Add a guard: if (x) { ... } or x?.foo.
  • “Type ‘X | undefined’ is not assignable to type ‘X’” — same idea; you didn’t narrow.

Fix: Read the message. It says exactly what’s wrong. Don’t reach for as Y (type assertion) until you’ve considered narrowing — assertions silence the compiler but the runtime can still crash.

Last resort: // @ts-expect-error <reason> on the offending line. Better than // @ts-ignore because it errors if the next line was actually fine.

Reference: TypeScript


”Hydration failed” / “Text content does not match”

What it means: React rendered HTML on the server, then on the client, and the two didn’t match.

Common causes:

  1. Time-dependent code — new Date().toString(), Date.now(). Server and client get different timestamps.
  2. Math.random() in render.
  3. Browser-only APIs — window, localStorage, document — used during render. Server has none of these.
  4. Conditional rendering on typeof window — server returns one thing, client another.

Fix:

  • Move the offending code into useEffect (runs only on client).
  • Or use a Client Component ("use client" at top of file).
  • For UI that genuinely must differ between server and client: render a placeholder during SSR, replace it after mount.

Reference: Next.js gotchas, The DOM — hydration


“‘X’ is defined but never used”

What it means: ESLint flagged an unused import or variable. By default it’s a warning, but some configs make it a build-failing error.

Fix:

  • Just delete the unused thing. Simplest path.
  • Prefix with _ to mark intentional: function foo(_unused: string).
  • Disable for one line: // eslint-disable-next-line @typescript-eslint/no-unused-vars.

Don’t disable the rule project-wide just to ship — the warning exists for a reason.

Reference: Linting


”ReferenceError: window is not defined” (Vercel-only)

What it means: Your code referenced window (or localStorage, document) during server-side execution. Vercel’s serverless runtime doesn’t have these.

Fix:

if (typeof window !== "undefined") {
  // browser-only code here
}

Or move the code into useEffect (Client Components only).

Reference: Client vs server


”Error: Failed to fetch dynamically imported module”

What it means: A code-split chunk couldn’t load. Usually after a deploy when the user’s tab is on the old code but the chunks have been replaced.

Fix:

  • In production: ask the user to refresh.
  • To prevent: Next.js + Vercel handle this automatically for most cases via service-worker style chunk versioning. If it persists, check your build output cache settings.

This isn’t usually your code’s fault — it’s a deploy-timing artifact.


”Build succeeded locally but failed on Vercel”

What it means: Your local environment is more permissive than Vercel’s.

Most common causes (in order):

  1. Case sensitivity. Local OS doesn’t care; Linux does. See “Module not found” above.
  2. Missing env var. Vercel doesn’t have the value local-only .env.local defines.
    • Fix: Vercel dashboard → Settings → Environment Variables → add the var → redeploy.
  3. TypeScript strict mode mismatch. Different tsconfig in CI, or a type that was inferred locally but errored in CI.
  4. Different Node version. Specify in package.json: "engines": { "node": "20.x" }.
  5. DevDependency used in production code. npm install --omit=dev skips dev deps. Move the dep into dependencies.

Reference: How-to: Debug a Vercel build failure, Vercel — common gotchas


”Cannot read properties of undefined (reading ‘X’)”

What it means: You tried to access .X on something that’s undefined. Classic JS error.

Fix:

  • Optional chaining: obj?.X returns undefined instead of crashing.
  • Nullish coalescing: obj?.X ?? "default".
  • Guard the access: if (obj) { obj.X }.

Find the cause: if the value should never be undefined, work backwards to where it could have been omitted. Often a missing prop, a forgotten await, or an API response that didn’t include the field.


”Maximum call stack size exceeded”

What it means: Infinite recursion. A function calls itself (directly or via a chain) without a base case.

Common cause: an event handler that fires the same event again. setState inside the render body (without a condition). Effects that update the state they depend on without a guard.

Fix: find the loop and add the base case. The error stack usually shows the same few function names repeating — that’s your loop.

Reference: Recursion


”ENOENT: no such file or directory”

What it means: Node tried to open a file that doesn’t exist.

Common causes:

  • Wrong path passed to fs.readFile or similar.
  • Working directory isn’t what you assumed. Use absolute paths: import.meta.url (modern), __dirname (CommonJS), or path.join(process.cwd(), ...).
  • File was generated at build time but isn’t included in deploy. Vercel only ships files that are imported or in public/.

”EADDRINUSE: address already in use :::3000”

What it means: Another process is using port 3000.

Fix:

# Windows PowerShell
Get-NetTCPConnection -LocalPort 3000 | Select-Object OwningProcess
Stop-Process -Id <pid>
# macOS / Linux
lsof -i :3000
kill <pid>

Or just use a different port: next dev -p 3001.


”Cannot find module ‘X’ or its corresponding type declarations”

What it means: You imported a package, but TypeScript can’t find the types.

Fix:

  • Install types: npm install -D @types/X.
  • Modern packages ship types built-in — if @types/X doesn’t exist, the package may already have types. Check package.json → "types" or "typings".
  • Custom modules without types: create X.d.ts with declare module "X";. Quick-and-dirty, but works.

Reference: TypeScript — type definitions for libraries


”Out of memory” during build

What it means: Node’s default memory limit (~2 GB on Windows, ~4 GB elsewhere) wasn’t enough.

Fix:

# Set higher memory limit for build
NODE_OPTIONS="--max-old-space-size=8192" npm run build
 
# Windows PowerShell
$env:NODE_OPTIONS="--max-old-space-size=8192"
npm run build

If this happens locally but not on Vercel, your code is probably fine — Vercel build runners have more RAM by default. If it happens on Vercel, you may need a Pro plan or to split your build.


See also


Sources