remix-run/react-router · error
Prerender (html): Received a
Error message
Prerender (html): Received a ${response.status} status code from `entry.server.tsx` while prerendering the `${pathname}` path.
${html} What it means
During HTML prerendering, React Router requests each document path from the built server. Redirect responses are handled separately (they emit a meta-refresh page), but after that branch any non-200 document response is fatal, because a static file whose content is an error page would be deployed as if it were the real page. The response HTML is appended to the message so the failing page (often the error boundary output) is visible.
Solutions
- Read the HTML in the error message to identify the error boundary content and underlying exception
- Fix or guard the loader/middleware so the path renders 200 at build time (env vars, external API guards, cached data)
- Remove intentionally-protected or erroring paths from the `prerender` config
- If the redirect branch is expected, make sure the loader returns a proper 3xx redirect rather than an error page
Example fix
// before (app/routes/docs.$.tsx)
export async function loader({ params }: Route.LoaderArgs) {
let doc = await fetch(`https://cms.example.com/${params['*']}`);
if (!doc.ok) return new Response('missing', { status: 404 });
return doc.json();
}
// after — exclude missing docs from prerender
// (react-router.config.ts)
export default {
ssr: true,
async prerender({ getStaticPaths }) {
let paths = await getStaticPaths();
return paths.filter(async (p) => !p.startsWith('/docs/deprecated/'));
},
}; Defensive patterns
Strategy: validation
Validate before calling
// Validate every prerender path against a running build before the real build
for (const p of PRERENDER_PATHS) {
const res = await fetch(`http://localhost:4173${p}`);
if (!res.ok && !(res.status >= 300 && res.status < 400)) {
console.warn(`Skipping ${p} — preview returned ${res.status}`);
}
} Try / catch
try {
await build();
} catch (e) {
if (e instanceof Error && /^Prerender \(html\)/.test(e.message)) {
// the message embeds the error-page HTML — locate the failing loader from it
}
} Prevention
- Keep prerender lists synchronized with routes (generate from route config where possible)
- Guard loaders with try/catch + cached fallback data for build-time resilience
- Run `vite preview` + a link checker before enabling prerender on large sites
When it happens
Trigger: A prerendered document path where rendering throws and the framework renders an error document with a 500 status; a loader returning a non-redirect error status for that path; middleware rejecting the request; entry.server.tsx returning a custom non-200 response for that route.
Common situations: Prerendering marketing/blog pages whose loaders fetch a CMS that fails or 404s in CI; auth-gated routes mistakenly listed in `prerender`; a root-level middleware returning 401 for all requests without the right headers.
Related errors
- ⚠️ Paths with dynamic/splat params cannot be prerendered…
- Prerender (data): Received a
- Prerender: Failed to start Vite preview server
- Prerender: Request failed for
- Prerender: Request failed for
AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18).
Data as JSON: /api/errors/2ae316845ce2741c.
Report an issue: GitHub.
Appendix: source
Thrown at packages/react-router-dev/vite/plugin.ts:2659
// A short delay causes Google to interpret the redirect as temporary.
// https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
let delay = response.status === 302 ? 2 : 0;
let escapedLocation = escapeHtml(location ?? "");
let escapedPathname = escapeHtml(pathname);
html = `<!doctype html>
<head>
<title>Redirecting to: ${escapedLocation}</title>
<meta http-equiv="refresh" content="${delay};url=${escapedLocation}">
<meta name="robots" content="noindex">
</head>
<body>
<a href="${escapedLocation}">
Redirecting from <code>${escapedPathname}</code> to <code>${escapedLocation}</code>
</a>
</body>
</html>`;
} else if (response.status !== 200) {
throw new Error(
`Prerender (html): Received a ${response.status} status code from ` +
`\`entry.server.tsx\` while prerendering the \`${pathname}\` ` +
`path.\n${html}`,
);
}
return [
{
path: `${pathname}/index.html`,
contents: html,
},
];
},
logFile(outputPath, metadata) {
invariant(viteConfig);
invariant(metadata);
// SPA fallback logging is handled in finalize after we know the final filename
if (metadata.type === "spa") {View on GitHub (pinned to 6beaca3952)