remix-run/react-router · error · Error
Prerender: Request timed out for ${prerenderPath}: ${error.m
Error message
Prerender: Request timed out for ${prerenderPath}: ${error.message} What it means
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`.
Source
Thrown at packages/react-router-dev/vite/plugins/prerender.ts:441
if (!response.ok) {
throw new Error(
`Prerender: Request failed for ${prerenderPath}: ${response.status} ${response.statusText}`,
);
}
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.View on GitHub (pinned to 1fd704a7da)
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.
Example fix
// before: loader fetches with no timeout
export const loader = () => fetch("https://slow-upstream/feed");
// after
export const loader = () =>
fetch("https://slow-upstream/feed", {
signal: AbortSignal.timeout(3000),
}); Defensive patterns
Strategy: retry
Validate before calling
// Bound every outbound fetch in loaders
export const loader = () =>
fetch(url, { signal: AbortSignal.timeout(3000) }); Try / catch
try {
await prerender(cfg);
} catch (e) {
if (/Request timed out/.test(String(e.message))) {
// tune timeout/concurrency and retry once
await prerender({ ...cfg, prerender: { ...cfg.prerender, concurrency: 1 } });
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
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/c82bbe2e3c2ceaab.
Report an issue: GitHub.