remix-run/react-router · error · Error

Prerender (html): Received a ${response.status} status code

Error message

Prerender (html): Received a ${response.status} status code from `entry.server.tsx` while prerendering the `${pathname}` path.\n${html}

What it means

Thrown in `postProcess` for standard HTML document renders (after redirect handling). When the response status is not 200 (and not a 3xx redirect handled earlier), the prerendered HTML would be an error page committed to disk, so React Router aborts the build. The full HTML is included for diagnosis.

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 1fd704a7da)

Solutions

  1. Visit the path in preview/serve to read the actual SSR error in the terminal log.
  2. Make the loader and render path deterministic at build time (no thrown errors for the prerendered URL).
  3. Remove the path from `prerender` if it legitimately cannot render statically.
  4. Check `entry.server.tsx` for unconditional non-200 responses.

Example fix

// before: loader throws for a prerendered page
export const loader = () => { throw new Response(null, { status: 500 }); };
// after
export const loader = () => json({ ok: true });
Defensive patterns

Strategy: validation

Validate before calling

const assertDocOk = (status: number, path: string) => {
  if (status !== 200) throw new Error(`Document ${path} returned ${status}`);
};

Prevention

When it happens

Trigger: A document route's loader throws (resulting in a 500 from `entry.server.tsx`); the route's render errors during SSR for a `prerender`-listed path; `entry.server.tsx` returns a non-200 `Response` (other than the redirect case already handled).

Common situations: Prerendering a static page whose loader throws on a missing CMS value; an error boundary renders a custom 500 page during SSR for the prerendered route; CI build with an env var unset causing SSR to fail.

Related errors


AI-assisted analysis of remix-run/react-router@1fd704a7da (2026-08-12). Data as JSON: /api/errors/2952d31bfa2c4a27. Report an issue: GitHub.