remix-run/react-router · error
Prerender: Request failed for
Error message
Prerender: Request failed for ${prerenderPath}: ${error.message} What it means
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.
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
Example fix
// before — default handleError rethrows everything
prerender({});
// after — tolerate transient socket errors with one retry
prerender({
handleError: async (request, error, { retry }) => {
if (/ECONNRESET|ECONNREFUSED/.test(error.message)) return retry();
throw error;
},
}); Defensive patterns
Strategy: retry
Validate before calling
// Health-check the preview server before issuing prerender requests
const ping = await fetch(base);
if (!ping.ok) throw new Error('Preview server unhealthy — aborting prerender'); Type guard
const isTransientNetworkError = (e: Error): boolean => /ECONNRESET|ECONNREFUSED|EPIPE|socket hang up/i.test(e.message);
Try / catch
try {
await prerender({});
} catch (e) {
if (e instanceof Error && isTransientNetworkError(e)) {
await prerender({}); // single retry for transient socket failures
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ⚠️ Paths with dynamic/splat params cannot be prerendered…
- Prerender (data): Received a
- Prerender: Failed to start Vite preview server
- Prerender (html): Received a
- Prerender: Request failed for
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/2a8bbbd21c65607c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-router-dev/vite/plugins/prerender.ts:446
return [
{
path: `${prerenderPath}/index.html`,
contents: await response.text(),
},
];
}
function defaultHandleError(request: Request, error: Error): void {
const prerenderPath = new URL(request.url).pathname;
if (request.signal?.aborted) {
throw new Error(
`Prerender: Request timed out for ${prerenderPath}: ${error.message}`,
);
}
throw new Error(
`Prerender: Request failed for ${prerenderPath}: ${error.message}`,
);
}
/**
* Issue prerender requests via `node:http` rather than the global `fetch`.
*
* Node's built-in `fetch` (undici) keeps a global dispatcher that pools
* keep-alive sockets. On Windows, exiting the build process with multiple
* pooled sockets to the Vite preview server still open triggers a libuv
* assertion (`!(handle->flags & UV_HANDLE_CLOSING)` in `src/win/async.c`)
* during teardown of the dispatcher's internal async handle. Closing or
* destroying the dispatcher does not clear the bad state.
*
* `node:http` without an explicit Agent closes each connection cleanly, so we
* use it here to avoid the assertion. Manual redirect handling is preserved.
*/
async function nodeHttpFetch(View on GitHub (pinned to 6beaca3952)