withastro/astro · error · Error

the prerender server responded

Error message

the prerender server responded ${response.status} ${response.statusText}${details}

What it means

During `astro build`, the Cloudflare adapter prerenders pages by fetching them from a local workerd-backed Vite preview server it starts on a random port. If that server answers with a non-OK status or no body, the build fails here; the message embeds the HTTP status and up to 200 characters of the (whitespace-collapsed) response body to expose the underlying failure. This is the Node-side mirror of a render failure that happened inside workerd.

Solutions

  1. Read the body snippet in the message — it usually contains the real stack trace from inside workerd.
  2. Reproduce locally with `wrangler dev` (or `astro dev` with the adapter's platform proxy) and open the failing page to get the full error.
  3. Search the failing page, its layout, and middleware for Node-only APIs and replace them with runtime-agnostic or web-standard alternatives.
  4. If the page is not meant to be prerendered, set `export const prerender = false` on it.
  5. Update @astrojs/cloudflare and astro to matching latest versions.
Defensive patterns

Strategy: try-catch

Try / catch

import { build } from 'astro';
try {
  await build({ root: './', logLevel: 'info' });
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  // messages embed 'the prerender server responded <status>' plus up to 200 chars of body
  if (msg.includes('prerender server responded')) {
    console.error('Workerd-side render failure — body snippet:', msg);
    process.exitCode = 1;
  } else throw e;
}

Prevention

When it happens

Trigger: Building with @astrojs/cloudflare while prerendering is enabled, and a page/middleware throws inside the workerd runtime, a route 404s on the internal server, or the response stream aborts so `response.ok` is false or `response.body` is null.

Common situations: A page works in `astro dev` (Node) but uses an API unavailable in workerd (fs, native modules, Node streams); middleware crashing for the prerendered route; a dependency with workerd-incompatible code paths; version mismatch between astro and the adapter.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/64f8ece20f44671c. Report an issue: GitHub.

Appendix: source

Thrown at packages/integrations/cloudflare/src/prerenderer.ts:148

	sourcePath: string | undefined,
): Promise<void> {
	const response = await fetch(createImageTransformUrl(serverUrl, originalPath, transform), {
		method: 'POST',
		// Remote images have no local original; the worker fetches those itself.
		...(sourcePath
			? {
					body: Readable.toWeb(createReadStream(sourcePath)) as unknown as BodyInit,
					// Required by Node's fetch whenever the body is a stream.
					duplex: 'half',
				}
			: {}),
	} as RequestInit);

	if (!response.ok || !response.body) {
		// The body can be a full error page, so keep only enough of it to be useful.
		const body = (await response.text().catch(() => '')).replace(/\s+/g, ' ').trim();
		const details = body ? `: ${body.slice(0, 200)}` : '';
		throw new Error(
			`the prerender server responded ${response.status} ${response.statusText}${details}`,
		);
	}

	const outputPath = join(fileURLToPath(clientDir), finalPath);
	await mkdir(dirname(outputPath), { recursive: true });
	// `fetch` types the body as the DOM `ReadableStream`, which is structurally
	// identical to but nominally distinct from the `node:stream/web` one.
	const body = response.body as unknown as NodeReadableStream<Uint8Array>;
	await pipeline(Readable.fromWeb(body), createWriteStream(outputPath));
}

/**
 * Creates a prerenderer that uses Cloudflare's workerd runtime via a preview server.
 * This allows prerendering to happen in the same runtime that will serve the pages.
 */
export function createCloudflarePrerenderer({
	root,

View on GitHub (pinned to e294953aa8)