Your local npm run build is green. Vercel says Build failed. Annoying, but the failure modes are limited: seven categories cover roughly 95% of real failures, and the build log tells you which one — if you know where to look. This guide gives the exact log strings, the one diagnostic command, and the config fix for each. Figures are current as of June 2026.
TL;DR
- Open the failed deployment, click Building → scroll to the last red line before the process exits. That line is the real cause ~90% of the time.
- The build runs in a clean Linux container on Node 24.x by default (you can pin
20.xor22.x). Your Mac runs a different Node, has your env vars, and is case-insensitive on file paths. Most failures come from one of those three gaps. - Each build container has 8192 MB (8 GB) of memory and a 45-minute hard timeout on every plan, including Pro — those two limits are not raised by upgrading the seat alone.
How to read the log line
Match the failing line to a category, then jump to the matching fix below.
Log string → Category
"Cannot find module" / "Module not found" → dependency drift or path case
"process is not defined" / undefined env → missing build-time env var
"Type error:" / "tsc" exits non-zero → TypeScript stricter than local
"Killed" / "SIGKILL" / "heap out of memory" → out of memory (OOM)
"No Output Directory named ... found" → wrong framework preset / output dir
"Command failed with exit code 1" → generic: read the line above it
Build runs ~45 min then stops → timeout (same on Hobby and Pro)
"worked yesterday, fails today, no code change"→ stale cache or dependency drift
The seven causes and their fixes
1. Node version mismatch
Vercel defaults new projects to Node 24.x (the available majors are 24.x, 22.x, and 20.x as of June 2026). If your code relies on something your local Node has but 24.x dropped — or you developed on 20 and a dependency needs 22+ — the build diverges from your machine. Pin it explicitly in package.json so the dashboard setting can never drift out from under you:
{
"engines": { "node": "24.x" },
"scripts": { "build": "astro build" }
}
Vercel reads engines.node and deploys the latest patch of that major. The Project Settings dropdown (Settings → Build and Deployment → Node.js Version) is the fallback when engines is absent. Set one or the other; don’t fight both.
2. Missing environment variable
The classic symptom is process is not defined or a value reading as undefined during the build (not at runtime). Vercel does not copy your local .env — variables live in the dashboard, scoped per environment. A var added to Preview but not Production fails only on production deploys, which is why “it built on my PR but failed on main” is so common.
vercel env add OPENAI_API_KEY production
# paste the secret when prompted
vercel env add SITE_URL production
# https://yourdomain.com
vercel --prod # trigger a fresh deploy
Dashboard path: Settings → Environment Variables, then add the var to every environment that runs a build. In Next.js, anything the browser reads must be prefixed NEXT_PUBLIC_ and must exist at build time — a missing NEXT_PUBLIC_* is a frequent exit code 1 cause.
3. TypeScript stricter on Vercel than locally
You see Type error: and a non-zero tsc exit. Usually your editor was silently tolerating something, or skipLibCheck masked it locally. Reproduce exactly what Vercel runs:
npx tsc --noEmit
# with "strict": true in tsconfig.json, this surfaces the same errors Vercel sees
Fix the code first. Only relax config if the strictness was genuinely unintentional — and relax narrowly, not the whole strict flag:
{
"compilerOptions": {
"strict": true,
"skipLibCheck": true,
"noUncheckedIndexedAccess": false
}
}
4. Dependency drift or path-case bug
Cannot find module or Module not found with green local builds usually means the lockfile committed to git doesn’t match what installed. Refresh it:
rm -rf node_modules package-lock.json
npm install
git add package-lock.json
git commit -m "fix: refresh lockfile"
Then on Vercel: Deployments → Redeploy → uncheck “Use existing build cache” to force a clean install. Watch for the sneakier variant: macOS file paths are case-insensitive, Vercel’s Linux containers are case-sensitive. import Foo from './Components/Foo' resolves on your Mac and fails on Vercel if the folder is actually components. Grep your imports for capitalization that doesn’t match the real path.
5. Out of memory (“Killed” / SIGKILL)
Each build container is capped at 8192 MB (8 GB). When the build overruns it, Node is killed with SIGKILL and the log shows Killed or JavaScript heap out of memory — often with no other context. Two levers, in order:
First, raise Node’s heap below the container ceiling. Vercel’s own recommendation is to leave headroom, so target 6 GB, not 8:
{
"scripts": {
"build": "NODE_OPTIONS='--max-old-space-size=6144' next build"
}
}
Second, cut peak memory in the build itself. For Astro, serialize page generation and avoid inlining everything:
// astro.config.mjs
export default defineConfig({
build: {
concurrency: 1, // serial page generation, lower peak RAM
inlineStylesheets: 'never',
},
image: { service: { entrypoint: 'astro/assets/services/sharp' } },
});
If you genuinely need more than 8 GB, Pro and Enterprise can enable on-demand enhanced builds (8 CPUs, 16 GB memory, 58 GB disk). Raising the seat to Pro alone does not double build memory — you have to turn enhanced builds on.
6. Wrong output directory / framework preset
The build itself “succeeds” but the deploy fails with No Output Directory named "public" found. Vercel expected static files in public but your framework writes elsewhere — Astro and Vite output dist, Next.js outputs .next. This happens when the framework preset was set wrong or a manual Output Directory override is stale.
Fix in Settings → Build and Deployment: set the Framework Preset to match your stack (Astro, Next.js, Vite, …) and clear any manual Output Directory override unless you truly need a custom one. Let the preset choose the directory.
7. Build timeout (45 minutes, every plan)
The Build Step has a 45-minute hard cap on all plans — Hobby and Pro alike. Upgrading the seat does not raise it. On a large content site you hit it by generating too many pages in one pass. Options:
- Split the build by locale or section and deploy them as separate projects.
- Generate only the N most recent items per build; archive the rest behind a separate deploy.
- Speed the build itself (faster image service, fewer redundant data fetches, cache between runs) so the same page count finishes under 45 minutes.
// astro.config.mjs — directory output, then cap pages generated per build
export default defineConfig({
build: { format: 'directory' },
});
Reproduce the failure locally before you push
A reproducible local failure cuts the redeploy loop from minutes to seconds. Match Vercel’s exact build path:
npm ci # clean install strictly from the lockfile
npm run build # same command Vercel runs
For an even closer match — Vercel’s own container environment — use the CLI:
vercel build --prod
vercel deploy --prebuilt --prod
Common pitfalls
- Reading only the bottom of the build log. The real error is often a few lines above the final “exit code 1”.
- Adding the env var to one environment (Preview) but not the one that’s actually failing (Production).
- Committing
node_modulesto “fix” a dependency error. It bloats the repo and slows every build; refresh the lockfile instead. - Disabling
strictglobally to dodge one type error. You’re hiding the next ten too. - Assuming Pro raises the 45-minute timeout or the 8 GB build memory automatically. The timeout is fixed on every plan; the memory bump requires turning on enhanced builds.
FAQ
- Where exactly is the real error in the log?: Open the deployment, expand the Building step, and find the last red line before the process exits with a non-zero code. The bottom-most “Command failed with exit code 1” is the symptom; the cause is usually one or two lines above it.
- Why does it build locally but fail on Vercel?: The three usual gaps — a different Node version (Vercel defaults to 24.x), a build-time env var that lives only on your machine, or a file path whose capitalization works on case-insensitive macOS but not on Vercel’s case-sensitive Linux.
- My build log just says “Killed” — what is that?: Out of memory. The container caps at 8 GB. Set
NODE_OPTIONS='--max-old-space-size=6144', reduce concurrency and image work, and only then consider Pro on-demand enhanced builds (16 GB). - Does upgrading to Pro fix a build timeout?: No. The 45-minute build cap is identical on Hobby and Pro. Split the build or make it faster; the seat upgrade won’t help here.
- Can I run Vercel’s build container on my machine?: Closely, yes:
vercel build --prodthenvercel deploy --prebuilt --prodreproduces Vercel’s build environment far better than a plainnpm run build.