{"record":{"id":"32c79516e80001bd","repo":"chenglou/pretext","slug":"timed-out-waiting-for-local-bun-server-on-baseur","errorCode":null,"errorMessage":"Timed out waiting for local Bun server on ${baseUrl}","messagePattern":"Timed out waiting for local Bun server on (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/accuracy-check.ts","lineNumber":101,"sourceCode":"function sleep(ms: number): Promise<void> {\n  return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nasync function canReachServer(baseUrl: string): Promise<boolean> {\n  try {\n    const response = await fetch(baseUrl)\n    return response.ok\n  } catch {\n    return false\n  }\n}\n\nasync function waitForServer(baseUrl: string): Promise<void> {\n  for (let i = 0; i < 200; i++) {\n    if (await canReachServer(baseUrl)) return\n    await sleep(100)\n  }\n  throw new Error(`Timed out waiting for local Bun server on ${baseUrl}`)\n}\n\nasync function startProxyServer(targetOrigin: string): Promise<{ baseUrl: string, server: HttpServer }> {\n  const port = await getAvailablePort()\n  const server = createHttpServer(async (req, res) => {\n    try {\n      const targetUrl = new URL(req.url ?? '/', targetOrigin)\n      const response = await fetch(targetUrl, { method: req.method ?? 'GET' })\n      res.statusCode = response.status\n      response.headers.forEach((value, key) => {\n        if (key.toLowerCase() === 'transfer-encoding') return\n        res.setHeader(key, value)\n      })\n      const body = response.body === null ? new Uint8Array(0) : new Uint8Array(await response.arrayBuffer())\n      res.end(body)\n    } catch (error) {\n      res.statusCode = 500\n      res.setHeader('content-type', 'text/plain; charset=utf-8')","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/chenglou/pretext/blob/ac49b09b7d83ede19581fa94a8b892b07d309baf/scripts/accuracy-check.ts#L83-L119","documentation":"Thrown by waitForServer() after 200 failed reachability checks (100ms apart = 20s total) against the local Bun dev server. canReachServer does an HTTP fetch and returns true only on response.ok; any connection refusal or non-2xx counts as not-ready. This gate exists specifically on the Firefox accuracy path (accuracy-check.ts:193-200) where Bun is spawned directly via zsh -lc and the script must wait for it to listen before proxying.","triggerScenarios":"The spawned process `bun --port=${bunPort} --no-hmr pages/*.html` fails to bind the port or crashes within 20 seconds. Concretely: bun is not installed or not on PATH under /bin/zsh -lc; the chosen port is already taken; a syntax/import error in pages/*.html causes bun to exit immediately; the OS is slow to allocate the socket.","commonSituations":"Running accuracy-check on a machine where bun is installed via nvm/asdf and the login shell PATH differs from the parent process; a previous Bun server did not release the port; a recent edit to an HTML/page module broke the dev server boot; heavy load making the 20s budget too tight.","solutions":["Run `bun --port=<port> --no-hmr pages/*.html` manually in the repo root and read the boot error.","Confirm `bun` resolves under `/bin/zsh -lc 'which bun'` — install it or fix the login shell PATH.","Free the target port (lsof -i :<port>) if a stale server holds it.","If boot is just slow, the 200x100ms budget is hardcoded — reduce other load or warm caches before re-running."],"exampleFix":"// before\nserverProcess = spawn('/bin/zsh', ['-lc', `bun --port=${bunPort} --no-hmr pages/*.html`], {\n  cwd: process.cwd(),\n  stdio: 'ignore',\n})\nawait waitForServer(bunBaseUrl)\n\n// after (capture boot errors instead of ignoring stdio)\nserverProcess = spawn('/bin/zsh', ['-lc', `bun --port=${bunPort} --no-hmr pages/*.html`], {\n  cwd: process.cwd(),\n  stdio: ['ignore', 'pipe', 'pipe'],\n})\nserverProcess.stdout?.on('data', d => process.stderr.write(`[bun] ${d}`))\nserverProcess.stderr?.on('data', d => process.stderr.write(`[bun!] ${d}`))\nawait waitForServer(bunBaseUrl)","handlingStrategy":"retry","validationCode":"// Sanity-check the bun binary and target dir before spawning.\nimport { which } from './util'\nif (await which('bun') === null) {\n  throw new Error('bun not found on PATH under /bin/zsh -lc')\n}","typeGuard":null,"tryCatchPattern":"// Capture bun's stderr so boot failures are diagnosable instead of a 20s timeout.\nserverProcess = spawn('/bin/zsh', ['-lc', `bun --port=${bunPort} --no-hmr pages/*.html`], {\n  cwd: process.cwd(),\n  stdio: ['ignore', 'pipe', 'pipe'],\n})\nconst bootErrors: string[] = []\nserverProcess.stderr?.on('data', chunk => bootErrors.push(String(chunk)))\ntry {\n  await waitForServer(bunBaseUrl)\n} catch (error) {\n  throw new Error(`${(error as Error).message}; bun stderr: ${bootErrors.join('') || '(none)'}`)\n}","preventionTips":["Verify `bun` resolves under `/bin/zsh -lc 'which bun'` in the same environment the script uses.","Free the target port before running (lsof -i :<port>).","Run the bun server command manually first to catch boot errors quickly.","Avoid spawning during heavy machine load where the 20s budget is too tight."],"tags":["accuracy-check","network","bun","firefox","process-spawn","timeout"],"backgroundTag":null,"analyzedSha":"ac49b09b7d83ede19581fa94a8b892b07d309baf","analyzedAt":"2026-08-12T17:03:16.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}