vercel/next.js · error
Server process exited before becoming ready (is port already
Error message
Server process exited before becoming ready (is port already in use?)
What it means
Thrown by waitForServerReady (benchmark.ts:308) when the spawned server child has already exited (exitCode !== null or signalCode !== null) before the readiness probe succeeds. This guard exists precisely because on a contested port a stale server can answer the probe while our child fails to bind — without this check the run would silently measure the wrong server. The message hints at EADDRINUSE because that's the most common cause of immediate child exit.
Source
Thrown at bench/render-pipeline/benchmark.ts:308
throw new Error(
`Missing ${NEXT_BIN}. Build Next.js first (pnpm --filter=next build).`
)
}
}
async function waitForServerReady(
url: string,
timeoutMs: number,
serverDied?: () => boolean
): Promise<void> {
const start = performance.now()
while (performance.now() - start < timeoutMs) {
// Without this check, a server that dies on startup (e.g. EADDRINUSE
// against a stale server on the same port) is indistinguishable from
// a slow one — worse, a 200 from whatever else owns the port would
// pass, and the run would silently measure the wrong server.
if (serverDied?.()) {
throw new Error(
`Server process exited before becoming ready (is port already in use?)`
)
}
try {
const response = await fetch(url, { cache: 'no-store' })
await response.arrayBuffer()
if (response.ok) return
} catch {
// server not ready yet
}
await sleep(200)
}
throw new Error(`Server did not become ready within ${timeoutMs}ms`)
}
function spawnedServerDied(server: ReturnType<typeof spawn>): () => boolean {
return () => server.exitCode !== null || server.signalCode !== null
}View on GitHub (pinned to 0ae8c72462)
Solutions
- Free the port: find and stop whatever holds it (the message's primary hint), then re-run.
- Pass a different --port to avoid the conflict entirely.
- For e2e, ensure `next build` ran successfully before `next start` (the benchmark builds by default; if you passed --build=false you must build manually).
- Reproduce the child's exit by running the same node command manually to see its stderr.
Example fix
# before — child exits on startup, port held by stale server # pnpm bench:render-pipeline --port=3199 # fix — free the port or pick another # (find holder) # lsof -i :3199 # or: ss -ltnp 'sport = :3199' # then either kill it or: # pnpm bench:render-pipeline --port=3200
Defensive patterns
Strategy: validation
Validate before calling
import { createConnection } from 'node:net'
async function isPortFree(port: number): Promise<boolean> {
return new Promise((resolveFn) => {
const sock = createConnection({ port, host: '127.0.0.1' })
sock.on('connect', () => { sock.destroy(); resolveFn(false) }) // someone is listening
sock.on('error', () => resolveFn(true))
})
}
if (!(await isPortFree(port))) throw new Error(`port ${port} is in use`) Type guard
function isServerDiedError(err: unknown): boolean {
return err instanceof Error && /Server process exited before becoming ready/.test(err.message)
} Try / catch
try {
await waitForServerReady(url, timeoutMs, spawnedServerDied(server))
} catch (err) {
if (isServerDiedError(err)) {
console.error('Server child exited during boot. Reproduce with the exact `node` command to see stderr.')
console.error('Common cause: EADDRINUSE — free the port or pass --port.')
}
throw err
} Prevention
- Always pass a free --port, or check the port is free before spawning.
- For e2e, ensure `next build` succeeded (the benchmark builds by default; --build=false skips it).
- Reproduce the child's startup manually to read its stderr, since the harness discards child stdout.
- Clean up servers in finally blocks so crashed runs don't leak processes onto the port.
When it happens
Trigger: The spawned `next start` / minimal-server child dies during boot: EADDRINUSE (another process on --port), a startup exception, missing .next build output for `next start`, or a missing NODE/module. spawnedServerDied() flips true once exitCode/signalCode is set, and the next loop iteration throws.
Common situations: A previous benchmark/server left a process on the port (assertPortFree should catch this first, but timing windows and processes bound to 0.0.0.0 vs 127.0.0.1 can slip through); running `next start` without a prior `next build`; a runtime error in minimal-server.js; signal from OOM killer.
Related errors
- Server did not become ready within ${timeoutMs}ms
- Port ${port} is already serving responses — another server i
- Server process exited before becoming ready (is port already
- Dev server did not become ready within ${timeoutMs}ms
- Request failed (${response.status}) for ${url}
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/b2686723e38e7a1c.
Report an issue: GitHub.