remix-run/react-router · error
Prerender: Request timed out for
Error message
Prerender: Request timed out for ${prerenderPath}: ${error.message} What it means
Each prerender request is issued with an `AbortSignal.timeout(...)` (default 10000ms, configurable via the prerender options `timeout`). The default `handleError` inspects `request.signal?.aborted`: if the abort fired, it rethrows as a timeout so you can distinguish slow pages from broken ones. Timeouts usually mean the page's loaders or render are genuinely slow (external API latency, large pages, cold caches) rather than broken.
Solutions
- Raise the per-request budget: pass a larger `timeout` (ms) in the prerender options
- Profile and speed up the slow loader(s): parallelize fetches, add caching, trim payload sizes
- Increase `concurrency` carefully or reduce prerendered paths to shorten overall wall time
- If a path can never finish in time, remove it from prerender and render it on demand
Example fix
// before
prerender({ /* default timeout 10000ms */ });
// after
prerender({ timeout: 60000 }); Defensive patterns
Strategy: retry
Validate before calling
// Time a representative path before choosing a timeout budget
const start = performance.now();
await fetch(`${base}${slowestPath}`);
const needed = performance.now() - start;
await prerender({ timeout: Math.ceil(needed * 2) }); Type guard
const isTimeoutError = (e: Error): boolean => /^Prerender: Request timed out/.test(e.message);
Try / catch
try {
await prerender({ timeout: 10000 });
} catch (e) {
if (e instanceof Error && isTimeoutError(e)) {
await prerender({ timeout: 60000 }); // one deliberate retry with a bigger budget
} else throw e;
} Prevention
- Set `timeout` proportional to your slowest loader + render
- Parallelize and cache loader fetches so no single path hangs
- Mark third-party calls with their own shorter AbortSignal timeouts
When it happens
Trigger: A prerendered path whose combined loaders + render exceed the timeout (default 10s) — slow third-party fetches, N+1 queries, huge HTML; running CI on constrained/slow machines; setting `timeout` too low in prerender options.
Common situations: Blog/store pages aggregating many API calls at build time; rate-limited APIs slowing builds; large path lists where concurrency compounds latency.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
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/c82bbe2e3c2ceaab.
Report an issue: GitHub.
Appendix: 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 6beaca3952)