{"record":{"id":"c82bbe2e3c2ceaab","repo":"remix-run/react-router","slug":"prerender-request-timed-out-for-prerenderpath","errorCode":null,"errorMessage":"Prerender: Request timed out for ${prerenderPath}: ${error.message}","messagePattern":"Prerender: Request timed out for (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugins/prerender.ts","lineNumber":441,"sourceCode":"  if (!response.ok) {\n    throw new Error(\n      `Prerender: Request failed for ${prerenderPath}: ${response.status} ${response.statusText}`,\n    );\n  }\n\n  return [\n    {\n      path: `${prerenderPath}/index.html`,\n      contents: await response.text(),\n    },\n  ];\n}\n\nfunction defaultHandleError(request: Request, error: Error): void {\n  const prerenderPath = new URL(request.url).pathname;\n\n  if (request.signal?.aborted) {\n    throw new Error(\n      `Prerender: Request timed out for ${prerenderPath}: ${error.message}`,\n    );\n  }\n\n  throw new Error(\n    `Prerender: Request failed for ${prerenderPath}: ${error.message}`,\n  );\n}\n\n/**\n * Issue prerender requests via `node:http` rather than the global `fetch`.\n *\n * Node's built-in `fetch` (undici) keeps a global dispatcher that pools\n * keep-alive sockets. On Windows, exiting the build process with multiple\n * pooled sockets to the Vite preview server still open triggers a libuv\n * assertion (`!(handle->flags & UV_HANDLE_CLOSING)` in `src/win/async.c`)\n * during teardown of the dispatcher's internal async handle. Closing or\n * destroying the dispatcher does not clear the bad state.","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/plugins/prerender.ts#L423-L459","documentation":"Each prerender request is issued with an `AbortSignal.timeout(...)` (default 10000ms, configurable via the prerender options `timeout`). The default `handleError` inspects `request.signal?.aborted`: if the abort fired, it rethrows as a timeout so you can distinguish slow pages from broken ones. Timeouts usually mean the page's loaders or render are genuinely slow (external API latency, large pages, cold caches) rather than broken.","triggerScenarios":"A prerendered path whose combined loaders + render exceed the timeout (default 10s) — slow third-party fetches, N+1 queries, huge HTML; running CI on constrained/slow machines; setting `timeout` too low in prerender options.","commonSituations":"Blog/store pages aggregating many API calls at build time; rate-limited APIs slowing builds; large path lists where concurrency compounds latency.","solutions":["Raise the per-request budget: pass a larger `timeout` (ms) in the prerender options","Profile and speed up the slow loader(s): parallelize fetches, add caching, trim payload sizes","Increase `concurrency` carefully or reduce prerendered paths to shorten overall wall time","If a path can never finish in time, remove it from prerender and render it on demand"],"exampleFix":"// before\nprerender({ /* default timeout 10000ms */ });\n\n// after\nprerender({ timeout: 60000 });","handlingStrategy":"retry","validationCode":"// Time a representative path before choosing a timeout budget\nconst start = performance.now();\nawait fetch(`${base}${slowestPath}`);\nconst needed = performance.now() - start;\nawait prerender({ timeout: Math.ceil(needed * 2) });","typeGuard":"const isTimeoutError = (e: Error): boolean =>\n  /^Prerender: Request timed out/.test(e.message);","tryCatchPattern":"try {\n  await prerender({ timeout: 10000 });\n} catch (e) {\n  if (e instanceof Error && isTimeoutError(e)) {\n    await prerender({ timeout: 60000 }); // one deliberate retry with a bigger budget\n  } else throw e;\n}","preventionTips":["Set `timeout` proportional to your slowest loader + render","Parallelize and cache loader fetches so no single path hangs","Mark third-party calls with their own shorter AbortSignal timeouts"],"tags":["prerender","ssg","timeout","build"],"backgroundTag":"prerender-request-timeout","analyzedSha":"6beaca39526d5716c3c112ebb0782765baa5a9ce","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}