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
- Run `next build` before `next start` in the same directory.
- Verify distDir in next.config.js matches the directory containing the build output.
- In Docker/CI, ensure the build output (e.g. .next) is present in the image/stage where the server starts.
- 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
- Always run `next build` before `next start`.
- Include the full distDir in Docker images / deploy artifacts.
- Keep distDir consistent between build and start.
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
- Could not find a production build in the '${this.distDir}' d
- Failed to deploy project ${linkRes.stdout} ${linkRes.stderr}
- Manifest file is empty
- Command failed: ${command} ${args.join(' ')} (exit ${code})
- Missing ${NEXT_BIN}. Build Next.js first (pnpm --filter=next
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/0f38aa2d2abd173b.
Report an issue: GitHub.