{"record":{"id":"b2686723e38e7a1c","repo":"vercel/next.js","slug":"server-process-exited-before-becoming-ready-is-po","errorCode":null,"errorMessage":"Server process exited before becoming ready (is port already in use?)","messagePattern":"Server process exited before becoming ready \\(is port already in use\\?\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"bench/render-pipeline/benchmark.ts","lineNumber":308,"sourceCode":"    throw new Error(\n      `Missing ${NEXT_BIN}. Build Next.js first (pnpm --filter=next build).`\n    )\n  }\n}\n\nasync function waitForServerReady(\n  url: string,\n  timeoutMs: number,\n  serverDied?: () => boolean\n): Promise<void> {\n  const start = performance.now()\n  while (performance.now() - start < timeoutMs) {\n    // Without this check, a server that dies on startup (e.g. EADDRINUSE\n    // against a stale server on the same port) is indistinguishable from\n    // a slow one — worse, a 200 from whatever else owns the port would\n    // pass, and the run would silently measure the wrong server.\n    if (serverDied?.()) {\n      throw new Error(\n        `Server process exited before becoming ready (is port already in use?)`\n      )\n    }\n    try {\n      const response = await fetch(url, { cache: 'no-store' })\n      await response.arrayBuffer()\n      if (response.ok) return\n    } catch {\n      // server not ready yet\n    }\n    await sleep(200)\n  }\n  throw new Error(`Server did not become ready within ${timeoutMs}ms`)\n}\n\nfunction spawnedServerDied(server: ReturnType<typeof spawn>): () => boolean {\n  return () => server.exitCode !== null || server.signalCode !== null\n}","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/vercel/next.js/blob/0ae8c72462952df163f1b1e0726641bc5b40dc93/bench/render-pipeline/benchmark.ts#L290-L326","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before — child exits on startup, port held by stale server\n#   pnpm bench:render-pipeline --port=3199\n\n# fix — free the port or pick another\n# (find holder)\n#   lsof -i :3199   # or: ss -ltnp 'sport = :3199'\n# then either kill it or:\n#   pnpm bench:render-pipeline --port=3200","handlingStrategy":"validation","validationCode":"import { createConnection } from 'node:net'\nasync function isPortFree(port: number): Promise<boolean> {\n  return new Promise((resolveFn) => {\n    const sock = createConnection({ port, host: '127.0.0.1' })\n    sock.on('connect', () => { sock.destroy(); resolveFn(false) }) // someone is listening\n    sock.on('error', () => resolveFn(true))\n  })\n}\nif (!(await isPortFree(port))) throw new Error(`port ${port} is in use`)","typeGuard":"function isServerDiedError(err: unknown): boolean {\n  return err instanceof Error && /Server process exited before becoming ready/.test(err.message)\n}","tryCatchPattern":"try {\n  await waitForServerReady(url, timeoutMs, spawnedServerDied(server))\n} catch (err) {\n  if (isServerDiedError(err)) {\n    console.error('Server child exited during boot. Reproduce with the exact `node` command to see stderr.')\n    console.error('Common cause: EADDRINUSE — free the port or pass --port.')\n  }\n  throw err\n}","preventionTips":["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."],"tags":["server","port-conflict","eaddrinuse","render-pipeline","startup","benchmark"],"analyzedSha":"0ae8c72462952df163f1b1e0726641bc5b40dc93","analyzedAt":"2026-08-06T19:44:29.143Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}