withastro/astro · error · AstroError
RewriteWithBodyUsed
RewriteWithBodyUsed
Error message
`Astro.rewrite()` cannot be used if the request body has already been read. If you need to read the body, first clone the request.
What it means
Astro.rewrite() builds the new internal Request by transferring the original request's method, headers, and unread body stream into it. Once the body has been consumed (e.g. by request.json()/text()/formData()), the stream is used up and cannot be forwarded, so copyRequest throws RewriteWithBodyUsed instead of silently producing a rewrite with an empty body. The message tells you the fix: read from a clone, since Request.clone() gives an independent copy of the stream.
Solutions
- Clone the request before reading the body: const cloned = context.request.clone(); read from cloned, then rewrite with the original request.
- If you must read and forward the same payload, construct the rewrite Request yourself with a re-serialized body (new Request(url, { method, headers, body: JSON.stringify(data) })).
- If the body is not actually needed after inspection, remove the body-reading code so the stream stays untouched.
Example fix
// before
export const onRequest = async (context, next) => {
const user = await context.request.json(); // consumes the body
if (!user.ok) return context.rewrite('/login');
return next();
};
// after
export const onRequest = async (context, next) => {
const cloned = context.request.clone();
const user = await cloned.json(); // original stream still unread
if (!user.ok) return context.rewrite('/login');
return next();
}; Defensive patterns
Strategy: validation
Validate before calling
export function canRewrite(request: Request): boolean {
return !request.bodyUsed;
}
// in middleware:
if (!canRewrite(context.request)) {
// body already consumed; do not call context.rewrite()
return Response.json({ error: 'request already consumed' }, { status: 400 });
} Try / catch
import { AstroError } from 'astro/errors';
try {
return context.rewrite(target);
} catch (err) {
if (err instanceof AstroError && err.code === 'RewriteWithBodyUsed') {
// recover by forwarding the payload manually
return context.rewrite(new Request(target, {
method: context.request.method,
headers: context.request.headers,
body: capturedBody, // the data you read earlier
}));
}
throw err;
} Prevention
- Make request.clone() the first statement of any middleware that reads a body.
- Only read bodies in leaf handlers, never in middleware that also rewrites.
- Treat request as single-use: after any await request.json()/text()/formData(), the stream is gone.
When it happens
Trigger: In a middleware, calling any body-reading API on context.request (await request.json(), .text(), .formData(), .arrayBuffer(), or reading request.body directly) and afterwards returning context.rewrite(new Request('/other-route')) or otherwise triggering copyRequest with that consumed request. Also hit when a helper inspects the POST payload for logging/auth and then the middleware chain rewrites.
Common situations: Auth middleware that parses a JSON login payload before rewriting to a route; request-logging middleware that dumps bodies; upgrading a redirect-based middleware to Astro.rewrite() in Astro 4.x/5.x; API-gateway style front controllers.
Related errors
- FetchState not found on APIContext. `next(payload)`…
- NoImageMetadata
- ResponseSentError
- Astro.cookies.set() was called after the cookies had…
- ⚠️ Astro could not optimize image
AI-assisted analysis of withastro/astro@157c500c38 (2026-08-27).
Data as JSON: /api/errors/71dfe809a60297be.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/routing/rewrite.ts:154
/**
* Utility function that creates a new `Request` with a new URL from an old `Request`.
*
* @param newUrl The new `URL`
* @param oldRequest The old `Request`
* @param isPrerendered It needs to be the flag of the previous routeData, before the rewrite
* @param logger
* @param routePattern
*/
export function copyRequest(
newUrl: URL,
oldRequest: Request,
isPrerendered: boolean,
logger: AstroLogger,
routePattern: string,
): Request {
const canHaveBody = oldRequest.method !== 'GET' && oldRequest.method !== 'HEAD';
if (canHaveBody && oldRequest.bodyUsed) {
throw new AstroError(AstroErrorData.RewriteWithBodyUsed);
}
return createRequest({
url: newUrl,
method: oldRequest.method,
body: canHaveBody ? oldRequest.body : undefined,
isPrerendered,
logger,
headers: isPrerendered ? {} : oldRequest.headers,
routePattern,
init: {
referrer: oldRequest.referrer,
referrerPolicy: oldRequest.referrerPolicy,
mode: oldRequest.mode,
credentials: oldRequest.credentials,
cache: oldRequest.cache,
redirect: oldRequest.redirect,
integrity: oldRequest.integrity,
signal: oldRequest.signal,View on GitHub (pinned to 157c500c38)