remix-run/react-router · error · Error
Prerender: Request failed for ${prerenderPath}: ${error.mess
Error message
Prerender: Request failed for ${prerenderPath}: ${error.message} What it means
Thrown by `defaultHandleError` for any prerender request error that is NOT an abort (i.e. `request.signal.aborted` is false). This is the catch-all for exceptions raised by loaders, renderers, or `entry.server.tsx` during the prerender pass. The error message forwards the underlying `error.message` for the failing path.
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 1fd704a7da)
Solutions
- Reproduce by running `react-router build` with `--logLevel info` to capture the original stack trace from the preview server.
- Wrap flaky loader logic in try/catch and return deterministic fallbacks at build time.
- Validate the shape of data returned by loaders before rendering.
- Provide a custom `handleError` in your prerender config to log full context (Sentry, etc.) instead of only `error.message`.
Example fix
// before: loader throws on missing data
export const loader = () => {
return JSON.parse(cachedBody).items.map(i => i.name);
};
// after
export const loader = () => {
try {
return JSON.parse(cachedBody).items.map(i => i.name);
} catch {
return [];
}
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Validate loader output shape before render
const assertShape = (data: unknown) => {
if (typeof data !== "object" || data === null || !("items" in data)) {
throw new Error("loader returned unexpected shape");
}
}; Try / catch
// Provide a custom handleError to log full context
export const prerender = {
handleError(request, error) {
console.error(request.url, error.stack);
// swallow during build if acceptable, else rethrow
},
}; Prevention
- Wrap flaky loader logic in try/catch with build-time fallbacks.
- Validate loader output shapes before rendering.
- Configure a custom `handleError` to capture full stack traces during the build.
When it happens
Trigger: A loader throws a synchronous or asynchronous exception (network error, parse error, null deref); the renderer throws because of bad data shape; `entry.server.tsx` raises; an upstream dependency errors mid-request without aborting the signal.
Common situations: Loaders that throw on malformed data; missing env vars causing `undefined` access; type mismatches between loader output and component props; third-party SDK throwing during build.
Related errors
- Prerender (data): Received a ${response.status} status code
- Prerender (resource): Received a ${response.status} status c
- Prerender (html): Received a ${response.status} status code
- Invalid route exports found when prerendering with `ssr:fals
- Prerender: Request failed for ${prerenderPath}: ${response.s
AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12).
Data as JSON: /api/errors/2a8bbbd21c65607c.
Report an issue: GitHub.