Skip to content

Fixing deployments that hang after the build step succeeds

Vercel deployment stuck in "Building" after the build succeeds, with checks and domain assignment pending? The cause is usually a Node.js process that does its work but never exits. This guide shows you how to unblock, diagnose, and resolve it.

Anna Z.
3 min read
Last updated April 28, 2026

If your deployment shows the build finishing but then sits forever in "Building" with no error, with checks and domain assignment stuck pending, this guide is for you.

Vercel waits for your build command to finish and exit before moving on to upload, checks, and domain assignment. If your build does its work but the underlying Node.js process never exits (because something like a timer, open connection, or background task is still running), Vercel has no way to know the build is done. The deployment stays in "Building" until you cancel it.

This usually doesn't show up locally. When you run the build on your laptop, you stop it with Ctrl+C and don't notice anything is wrong. On Vercel there's nothing to send that signal.

The fix is to force your build process to exit once your framework is done.

Open the build logs for the stuck deployment. You should see:

  • Your framework's success messages (pages prerendered, server built, output generated, etc.).
  • Logs end shortly after that, with no "Build Completed" line.
  • No errors anywhere in the build.

If you see actual errors or the build stops part-way through, this isn't the right guide.

Add a small hook to your config that forces the build to exit cleanly once your framework finishes. Pick the snippet for your setup.

Nuxt (nuxt.config.ts):

export default defineNuxtConfig({
nitro: {
hooks: {
compiled() {
setTimeout(() => process.exit(0), 0)
}
}
}
})

Vite (vite.config.ts):

import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
{
name: 'force-exit-after-build',
apply: 'build',
closeBundle() {
setTimeout(() => process.exit(0), 0)
}
}
]
})

Other frameworks: look for a hook called something like closeBundle, done, compiled, or onBuildEnd, and call setTimeout(() => process.exit(0), 0) from it.

Commit, push, and your next deployment should complete. Your site is now unblocked.

This is a workaround, not a permanent fix. It tells Node "we're done, stop waiting" without addressing what was keeping it busy. For most teams that's fine; if you want to find and fix the underlying cause, continue to step 2.

Install a small diagnostic tool that prints what's still running when your build finishes:

npm install --save-dev why-is-node-running

Then update the same hook to print the diagnostic output before exiting.

Nuxt:

import whyIsNodeRunning from 'why-is-node-running'
export default defineNuxtConfig({
nitro: {
hooks: {
compiled() {
whyIsNodeRunning()
setTimeout(() => process.exit(0), 0)
}
}
}
})

Vite:

import whyIsNodeRunning from 'why-is-node-running'
export default defineConfig({
plugins: [
{
name: 'diagnose-hanging-build',
apply: 'build',
closeBundle() {
whyIsNodeRunning()
setTimeout(() => process.exit(0), 0)
}
}
]
})

Deploy once and check your build logs. The diagnostic output will name the file or module keeping the process alive. It's almost always one of:

  • A third-party plugin or module doing background work (analytics, error reporting, image uploads, telemetry).
  • A piece of your own server code that opens a connection or starts a timer at module load.

Based on what step 2 surfaced:

  • A third-party module with a config option: look for a setting like telemetry: false, enabled: false in production, or similar. Disable the background behavior in your build config.
  • A third-party module with no config option: keep the workaround from step 1 in place and file an issue on the module's repo. The diagnostic output from step 2 is exactly what their maintainers need.
  • Your own code: clean up the resource (clear the timer, close the connection, finish the pending task) before the build hook runs.

If your deployments worked until recently and you're not sure why this started, compare your last working commit to the broken one and look at:

  • Your framework config file (nuxt.config.*, vite.config.*, etc.)
  • New or upgraded packages in package.json
  • Any new server middleware, plugins, or integrations (especially analytics, monitoring, or error reporting)

A newly added package is the most common cause.

Was this helpful?

supported.

Read related documentation

No related documentation available.

Explore more guides

No related guides available.