remix-run/react-router · error

Prerender: Request failed for

Error message

Prerender: Request failed for ${prerenderPath}: ${response.status} ${response.statusText}

What it means

The prerender plugin ships a `defaultPostProcess` used when no custom postprocess is configured: it requests each path from a Vite preview server of your built app and rejects any non-OK response. This is the last line of defense so error pages are never written out as static HTML; unlike the framework-mode postProcess variants, it does not embed the body, only status and statusText.

Solutions

  1. Reproduce with `react-router-serve ./build/server/index.js` (or vite preview) and curl the exact path to see the real error
  2. Fix the loader/middleware so the path returns 200, or drop the path from the prerender list
  3. Provide a custom `postprocess` hook if you need different status handling (e.g. allow specific non-200s)

Example fix

// before — default postprocess rejects any non-200
prerender({ postProcess: undefined });

// after — custom postprocess that tolerates 404s
prerender({
  postProcess: async (request, response) => {
    if (response.status === 404) return []; // skip, don't emit
    return defaultPostProcess(request, response);
  },
});
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-validate all paths against a preview of the build
const results = await Promise.all(
  paths.map(async (p) => [p, (await fetch(`${base}${p}`)).ok] as const),
);
const goodPaths = results.filter(([, ok]) => ok).map(([p]) => p);

Try / catch

try {
  await prerender({ paths: goodPaths });
} catch (e) {
  if (e instanceof Error && /^Prerender: Request failed/.test(e.message)) {
    // retry once, then report the failing path + status from the message
  }
}

Prevention

When it happens

Trigger: Using the prerender plugin with its default postprocess where any requested path returns 4xx/5xx from the preview server (loader throw, middleware rejection, entry.server error); a request that gets redirected into a 404; wrong casing in the requested path on a case-sensitive server.

Common situations: Library/tool authors embedding `@react-router/dev`'s prerender plugin; prerendering pages whose loaders fail in CI; case-sensitivity differences between macOS dev and Linux CI.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/42bddb2cf447683e. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-router-dev/vite/plugins/prerender.ts:424

            },
          },
        };
      },
    },
    configResolved(resolvedConfig) {
      viteConfig = resolvedConfig;
    },
  };
}

async function defaultPostProcess(
  request: Request,
  response: Response,
): Promise<PrerenderFile[]> {
  const prerenderPath = new URL(request.url).pathname;

  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}`,

View on GitHub (pinned to 6beaca3952)