{"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":"Error","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/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/react-router-dev/vite/plugins/prerender.ts#L423-L459","documentation":"Thrown by `defaultHandleError` when a prerender request errors and its `request.signal` is `aborted`. The abort path indicates the request exceeded the prerender timeout (configured via `prerender.concurrency`/per-request timeout) and Vite cancelled it. The error message surfaces the underlying abort reason via `error.message`.","triggerScenarios":"A loader or renderer takes longer than the configured prerender timeout; an outbound fetch in a loader hangs (no timeout of its own); a slow CMS/database call during build; `prerender.concurrency` too high so requests queue past the per-request deadline.","commonSituations":"Large dynamic routes that fetch heavy data on each prerender; loaders calling external APIs without a timeout; CI environments with cold upstreams; concurrency set higher than the upstream can serve.","solutions":["Add a timeout to any outbound fetch in loaders (e.g. `AbortSignal.timeout(2000)`).","Cache loader responses during the build so repeated prerenders reuse data.","Tune `prerender.concurrency` downward to reduce contention.","Profile slow loaders and precompute their data into static files consumed at build time."],"exampleFix":"// before: loader fetches with no timeout\nexport const loader = () => fetch(\"https://slow-upstream/feed\");\n// after\nexport const loader = () =>\n  fetch(\"https://slow-upstream/feed\", {\n    signal: AbortSignal.timeout(3000),\n  });","handlingStrategy":"retry","validationCode":"// Bound every outbound fetch in loaders\nexport const loader = () =>\n  fetch(url, { signal: AbortSignal.timeout(3000) });","typeGuard":null,"tryCatchPattern":"try {\n  await prerender(cfg);\n} catch (e) {\n  if (/Request timed out/.test(String(e.message))) {\n    // tune timeout/concurrency and retry once\n    await prerender({ ...cfg, prerender: { ...cfg.prerender, concurrency: 1 } });\n  } else throw e;\n}","preventionTips":["Add explicit timeouts to all outbound fetches inside loaders.","Cache loader data during the build to avoid repeated slow fetches.","Tune `prerender.concurrency` down when upstreams are slow."],"tags":["prerender","timeout","loader","build","abort"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}