Read the log, not the UI
The Vercel dashboard summarizes with a red X and a two-line reason. The actual failure is always in the deploy log, and always at the FIRST non-zero exit — later errors are usually cascading noise from the first one. Open the failed deployment, click Build Logs, and search the log for 'error' with Cmd/Ctrl-F. Stop at the earliest match. Ninety percent of the time that first hit is the entire story. The other ten percent, the first error is a misleading warning and the true cause is a stack trace three lines down; read the surrounding context before you commit to a fix.
1. Node version mismatch
Symptom: 'Unexpected token' on syntax your local Node supports, or a package error like 'Package X requires Node >=20'. Fix: set the Node version explicitly in Project Settings → Node.js Version, and mirror it in package.json under engines.node. Do not rely on Vercel's default — it drifts over time as Vercel upgrades their build image, and a working build can silently break after a platform update. Once pinned, review the pin every quarter; a stale Node version eventually stops receiving security patches and Vercel will force an upgrade.
2. Missing environment variable
Symptom: 'process.env.X is undefined' during build, often inside getStaticProps, generateStaticParams, or a top-level module init. Fix: add the variable in Project Settings → Environment Variables and re-deploy. Preview, Production, and Development are separate scopes — a var set only for Production will not reach a PR preview build. If a variable is truly required for the site to render, add a build-time guard that throws with a clear message, so the failure is loud instead of a mystery undefined downstream.
3. Out-of-memory during build
Symptom: 'JavaScript heap out of memory' or the build dies mid-log with no clear cause and an exit code 137 (SIGKILL). Fix: bump the Node memory ceiling with NODE_OPTIONS='--max-old-space-size=4096' as a build-scoped env var. On free tier this can still hit the platform ceiling — trim your dependency graph, split routes, or upgrade to Pro. Very large Next.js apps sometimes benefit from setting experimental.workerThreads: false to reduce peak memory, at the cost of slower builds.
4. Wrong output directory
Symptom: 'No Output Directory named `dist` found' or `.next` for the wrong framework. Fix: match Project Settings → Build & Development → Output Directory to what your framework actually produces. Vite is dist. Next.js is .next. Astro is dist. SvelteKit varies by adapter. Remix varies by adapter. If you recently switched frameworks or upgraded a major version, this is almost always the cause. Set the framework preset first and let Vercel infer the defaults before overriding anything.
5. Monorepo path error
Symptom: 'Cannot find package.json' or a build that runs the wrong workspace. Fix: set Root Directory in Project Settings to the correct workspace path (e.g. apps/web). Also verify the build command uses the workspace-scoped variant — pnpm --filter web build, turbo run build --filter=web, nx build web. Vercel will happily run `pnpm build` from the monorepo root and then fail with a confusing error because there is no build script at the root. Explicit filter flags fix this.
Bonus failure modes worth knowing
- Case-sensitive filenames — Linux enforces case, macOS does not. `import Header from './header'` succeeds locally and fails on Vercel if the file is Header.tsx.
- Peer dependency conflicts — pnpm and npm handle these differently; pinning package-manager version in package.json prevents drift.
- Missing native binaries — sharp, esbuild, and @swc/core ship platform-specific binaries; running with --frozen-lockfile on Vercel can miss a binary if your lockfile was generated on a different OS.
- Environment-specific feature flags — a flag enabled only in Production can fail an SSR path that Preview never exercised.
Quick triage checklist
- Open the deploy log and Cmd/Ctrl-F for 'error'. Stop at the first hit.
- Compare Node version in the log with your local (node -v).
- Confirm every env var referenced in the failing step exists for that environment.
- If the log ends abruptly with no error, assume OOM until proven otherwise.
- If everything else looks fine, redeploy without cache — sometimes the build cache is corrupt.
- If a recent Vercel platform change coincides with the break, check the Vercel status page and their changelog.
What to do when nothing on the list applies
About one in twenty build fails don't fit any of the standard buckets. When you're stuck, do three things: reproduce locally with the exact Node version and env vars from Vercel, run the build with `vercel build --debug` to get the same output Vercel sees, and diff the last successful commit against the failing one. Nine out of ten mystery fails come down to a dependency bump, a lockfile change, or a subtle path rewrite that only bites in a clean build environment. If the diff is truly empty, redeploy without cache — very rarely, a corrupt build cache is the entire story.