{"record":{"id":"2a8bbbd21c65607c","repo":"remix-run/react-router","slug":"prerender-request-failed-for-prerenderpath-2a8bbb","errorCode":null,"errorMessage":"Prerender: Request failed for ${prerenderPath}: ${error.message}","messagePattern":"Prerender: Request failed for (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/vite/plugins/prerender.ts","lineNumber":446,"sourceCode":"\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.\n *\n * `node:http` without an explicit Agent closes each connection cleanly, so we\n * use it here to avoid the assertion. Manual redirect handling is preserved.\n */\nasync function nodeHttpFetch(","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/remix-run/react-router/blob/6beaca39526d5716c3c112ebb0782765baa5a9ce/packages/react-router-dev/vite/plugins/prerender.ts#L428-L464","documentation":"When a prerender request throws before a response is produced (network error, DNS failure, connection reset to the preview server, aborted for a non-timeout reason), the default `handleError` rethrows with the path and underlying `error.message`. Unlike the timeout twin, this indicates the request itself failed — the preview server died, the connection dropped, or fetch-level machinery errored — rather than the page being slow.","triggerScenarios":"The Vite preview server crashing or restarting mid-prerender; `ECONNREFUSED`/`ECONNRESET` because the server exited early; custom fetch adapters (the plugin can use node:http instead of global fetch) misconfigured; proxy or firewall interference on localhost.","commonSituations":"Preview server running out of memory while rendering many paths; antivirus/firewall blocking loopback connections on Windows; wrapper tooling killing the server on first output.","solutions":["Read `error.message` — `ECONNRESET`/`ECONNREFUSED` points at the preview server; reproduce with `vite preview` and curl the same path","Reduce memory pressure: fewer concurrent prerenders, smaller pages, or `--max-old-space-size` increase for the build process","Check that nothing kills the preview server during the build (watchers, supervisors, Windows firewall prompts)","Retry the build once to distinguish transient socket issues from deterministic failures"],"exampleFix":"// before — default handleError rethrows everything\nprerender({});\n\n// after — tolerate transient socket errors with one retry\nprerender({\n  handleError: async (request, error, { retry }) => {\n    if (/ECONNRESET|ECONNREFUSED/.test(error.message)) return retry();\n    throw error;\n  },\n});","handlingStrategy":"retry","validationCode":"// Health-check the preview server before issuing prerender requests\nconst ping = await fetch(base);\nif (!ping.ok) throw new Error('Preview server unhealthy — aborting prerender');","typeGuard":"const isTransientNetworkError = (e: Error): boolean =>\n  /ECONNRESET|ECONNREFUSED|EPIPE|socket hang up/i.test(e.message);","tryCatchPattern":"try {\n  await prerender({});\n} catch (e) {\n  if (e instanceof Error && isTransientNetworkError(e)) {\n    await prerender({}); // single retry for transient socket failures\n  } else throw e;\n}","preventionTips":["Give the build process enough memory so the preview server survives long renders","Disable overzealous watch/supervisor tooling around the build","Allow loopback connections in CI firewall rules"],"tags":["prerender","ssg","network","build"],"backgroundTag":"prerender-request-failed","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"}