{"record":{"id":"71dfe809a60297be","repo":"withastro/astro","slug":"rewritewithbodyused","errorCode":"RewriteWithBodyUsed","errorMessage":"`Astro.rewrite()` cannot be used if the request body has already been read. If you need to read the body, first clone the request.","messagePattern":"`Astro\\.rewrite\\(\\)` cannot be used if the request body has already been read\\. If you need to read the body, first clone the request\\.","errorType":"exception","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/routing/rewrite.ts","lineNumber":154,"sourceCode":"/**\n * Utility function that creates a new `Request` with a new URL from an old `Request`.\n *\n * @param newUrl The new `URL`\n * @param oldRequest The old `Request`\n * @param isPrerendered It needs to be the flag of the previous routeData, before the rewrite\n * @param logger\n * @param routePattern\n */\nexport function copyRequest(\n\tnewUrl: URL,\n\toldRequest: Request,\n\tisPrerendered: boolean,\n\tlogger: AstroLogger,\n\troutePattern: string,\n): Request {\n\tconst canHaveBody = oldRequest.method !== 'GET' && oldRequest.method !== 'HEAD';\n\tif (canHaveBody && oldRequest.bodyUsed) {\n\t\tthrow new AstroError(AstroErrorData.RewriteWithBodyUsed);\n\t}\n\treturn createRequest({\n\t\turl: newUrl,\n\t\tmethod: oldRequest.method,\n\t\tbody: canHaveBody ? oldRequest.body : undefined,\n\t\tisPrerendered,\n\t\tlogger,\n\t\theaders: isPrerendered ? {} : oldRequest.headers,\n\t\troutePattern,\n\t\tinit: {\n\t\t\treferrer: oldRequest.referrer,\n\t\t\treferrerPolicy: oldRequest.referrerPolicy,\n\t\t\tmode: oldRequest.mode,\n\t\t\tcredentials: oldRequest.credentials,\n\t\t\tcache: oldRequest.cache,\n\t\t\tredirect: oldRequest.redirect,\n\t\t\tintegrity: oldRequest.integrity,\n\t\t\tsignal: oldRequest.signal,","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/withastro/astro/blob/157c500c38faa7ecf1251adbaeefcd109470d75c/packages/astro/src/core/routing/rewrite.ts#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nexport const onRequest = async (context, next) => {\n  const user = await context.request.json(); // consumes the body\n  if (!user.ok) return context.rewrite('/login');\n  return next();\n};\n\n// after\nexport const onRequest = async (context, next) => {\n  const cloned = context.request.clone();\n  const user = await cloned.json(); // original stream still unread\n  if (!user.ok) return context.rewrite('/login');\n  return next();\n};","handlingStrategy":"validation","validationCode":"export function canRewrite(request: Request): boolean {\n  return !request.bodyUsed;\n}\n\n// in middleware:\nif (!canRewrite(context.request)) {\n  // body already consumed; do not call context.rewrite()\n  return Response.json({ error: 'request already consumed' }, { status: 400 });\n}","typeGuard":null,"tryCatchPattern":"import { AstroError } from 'astro/errors';\n\ntry {\n  return context.rewrite(target);\n} catch (err) {\n  if (err instanceof AstroError && err.code === 'RewriteWithBodyUsed') {\n    // recover by forwarding the payload manually\n    return context.rewrite(new Request(target, {\n      method: context.request.method,\n      headers: context.request.headers,\n      body: capturedBody, // the data you read earlier\n    }));\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["astro","middleware","rewrite","request-body","streaming"],"backgroundTag":"request-body-already-consumed","analyzedSha":"157c500c38faa7ecf1251adbaeefcd109470d75c","analyzedAt":"2026-08-27T22:42:12.133Z","contentChangedAt":"2026-08-27T22:42:12.133Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}