vercel/next.js · critical · Error

Could not find a production build in the '${opts.config.dist

Error message

Could not find a production build in the '${opts.config.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id

What it means

Thrown by the production router filesystem setup when it cannot read the BUILD_ID file from the configured distDir. The build ID file is the marker that a `next build` has completed; its absence (ENOENT) means no production build exists in that directory.

Source

Thrown at packages/next/src/server/lib/router-utils/filesystem.ts:201

    redirects: [],
    rewrites: {
      beforeFiles: [],
      afterFiles: [],
      fallback: [],
    },
    onMatchHeaders: [],
    headers: [],
  }
  let buildId = 'development'
  let previewProps: __ApiPreviewProps

  if (!opts.dev) {
    const buildIdPath = path.join(opts.dir, opts.config.distDir, BUILD_ID_FILE)
    try {
      buildId = await fs.readFile(buildIdPath, 'utf8')
    } catch (err: any) {
      if (err.code !== 'ENOENT') throw err
      throw new Error(
        `Could not find a production build in the '${opts.config.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id`
      )
    }

    try {
      for (const file of await recursiveReadDir(publicFolderPath)) {
        // Ensure filename is encoded and normalized.
        publicFolderItems.add(encodeURIPath(normalizePathSep(file)))
      }
    } catch (err: any) {
      if (err.code !== 'ENOENT') {
        throw err
      }
    }

    try {
      for (const file of await recursiveReadDir(legacyStaticFolderPath)) {
        // Ensure filename is encoded and normalized.

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Run `next build` before `next start` in the same directory.
  2. Verify distDir in next.config.js matches the directory containing the build output.
  3. In Docker/CI, ensure the build output (e.g. .next) is present in the image/stage where the server starts.
  4. Do not delete or git-ignore the BUILD_ID file inside distDir.

Example fix

// before: starting without building
next start

// after: build then start
next build && next start
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'fs'
import path from 'path'
const buildId = path.join(process.cwd(), '.next', 'BUILD_ID')
if (!fs.existsSync(buildId)) {
  throw new Error('Run `next build` before `next start`')
}

Type guard

import fs from 'fs'
function hasProductionBuild(distDir: string): boolean {
  return fs.existsSync(path.join(distDir, 'BUILD_ID'))
}

Try / catch

null

Prevention

When it happens

Trigger: Running `next start` (or a production server) without first running `next build`, or with a custom distDir that points somewhere the build output isn't. The readFile of BUILD_ID throws ENOENT.

Common situations: Deploying without a build step, CI that runs start before build, a Docker image that copied source but not .next, or a misconfigured distDir (e.g. 'out' while build wrote to '.next'). Deleting .next between build and start.

Related errors


AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06). Data as JSON: /api/errors/0f38aa2d2abd173b. Report an issue: GitHub.