How to Fix: TypeError: Response.clone: Body has already been consumed.
Fixing TypeError: Response.clone: Body has already been consumed in Web Fetch Handlers
This error means your code is trying to read, clone, forward, log, or parse a Fetch API response body more than once. In platforms built on modern web runtimes, including deployments that rely on the standard Request/Response model, a response body is usually a single-use stream. Once it has been consumed by json(), text(), arrayBuffer(), or similar methods, calling clone() or reading it again can fail with the exact message: Response.clone: Body has already been consumed.
Understanding the Root Cause
The underlying issue is not randomness. It is usually a timing-sensitive stream consumption bug.
In the Fetch API, both Request and Response bodies are often implemented as ReadableStream instances. Streams are designed to be consumed once unless you explicitly duplicate them before reading. That means all of the following can consume the body:
- await response.json()
- await response.text()
- await response.blob()
- await response.arrayBuffer()
- Passing the response into framework internals that read it for you
- Logging middleware that parses the body for debugging
After that first read, the runtime marks the body as used. If your code later does something like response.clone(), returns that response to another layer, or tries to parse it again, the runtime throws this error.
A common pattern that triggers the bug looks like this:
const response = await fetch(url);
const data = await response.json();
const copy = response.clone(); // Fails because the body was already consumed
Another common pattern is more indirect:
- You fetch data in a route handler or middleware.
- You parse the response body for validation or logging.
- You then try to forward the original Response object unchanged.
That fails because the forwarded response no longer has a readable body.
If the issue appears intermittent, it is often because only certain code paths read the body first. For example:
- An error branch reads response.text() for diagnostics.
- A success branch returns the original response.
- A monitoring tool conditionally inspects payloads.
- A helper function parses the body without documenting that side effect.
So the real root cause is: the same response body is being consumed by more than one consumer, sometimes directly, sometimes through abstraction layers.
Step-by-Step Solution
The fix is to decide who owns the body read and ensure it happens only once, or clone the response before any read operation.
1. Search for every body read
Start by checking all places where the response may be consumed:
response.json()
response.text()
response.blob()
response.arrayBuffer()
response.formData()
Also inspect wrappers, middleware, logging utilities, and error handlers.
2. Clone before consuming, not after
If you need one version for parsing and another for returning or separate processing, clone immediately after fetch:
const response = await fetch(url);
const responseForParsing = response.clone();
const data = await responseForParsing.json();
return response;
This works because both streams are created before either one is consumed.
3. Prefer parsing once and rebuilding the response
In many apps, the safest approach is to read the upstream response once, then construct a new response from the parsed or raw payload.
const upstream = await fetch(apiUrl);
const text = await upstream.text();
return new Response(text, {
status: upstream.status,
headers: upstream.headers
});
This pattern is especially useful when you need to inspect, transform, sanitize, or log the payload.
4. If you need JSON, parse once and return a fresh JSON response
const upstream = await fetch(apiUrl, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await upstream.json();
if (!upstream.ok) {
return Response.json({
error: data.error || 'Upstream request failed'
}, { status: upstream.status });
}
return Response.json(data, { status: upstream.status });
This avoids passing around a half-consumed response object.
5. Avoid double reads in error handling
A subtle bug happens when code reads the body in both success and failure paths.
Problematic code:
const response = await fetch(url);
if (!response.ok) {
const errorText = await response.text();
throw new Error(errorText);
}
const data = await response.json();
This is only safe if the error path always throws and no later code touches the same response. But helper refactors often break that assumption.
Safer version:
const response = await fetch(url);
const text = await response.text();
if (!response.ok) {
throw new Error(text || 'Request failed');
}
const data = JSON.parse(text);
6. Be careful with helper functions
If you pass a Response into helpers, document whether they consume the body.
Bad abstraction:
async function logResponse(response) {
console.log(await response.text());
}
const response = await fetch(url);
await logResponse(response);
return response; // Body already consumed
Better abstraction:
async function logResponse(response) {
const clone = response.clone();
console.log(await clone.text());
}
const response = await fetch(url);
await logResponse(response);
return response;
7. Add defensive debugging
During debugging, check the bodyUsed property before reading:
const response = await fetch(url);
console.log('Before read:', response.bodyUsed);
const data = await response.json();
console.log('After read:', response.bodyUsed);
If you see bodyUsed === true earlier than expected, another part of your code is consuming the body first.
8. Use a stable proxy pattern when forwarding upstream responses
If your handler behaves like a proxy, read once and return a new response rather than returning the original after inspection.
export async function GET() {
const upstream = await fetch('https://example.com/api/data');
const body = await upstream.text();
return new Response(body, {
status: upstream.status,
headers: new Headers(upstream.headers)
});
}
This is one of the most reliable fixes when the original response may be touched by observability, auth, or transformation logic.
Common Edge Cases
Middleware or instrumentation reads the body
If you use analytics, tracing, error monitoring, or custom wrappers around fetch, they may parse the body for logs. Your application code then becomes the second consumer.
Fix: clone inside the instrumentation layer or log only metadata like status and headers.
Mixing text() and json()
Developers often call text() for debugging and later call json(). That is a guaranteed double read.
Fix: read once as text, then JSON.parse(text) if needed.
Returning a consumed response from a server handler
If a route handler reads the upstream body and then returns the original response object, frameworks that try to stream it to the client can throw this error.
Fix: construct a new Response after reading.
Cloning too late
response.clone() must happen before any body consumption. Doing it after json() or text() is already too late.
Conditional code paths
The bug may look random when only some branches consume the body, such as:
- Debug mode enabled
- Non-200 responses
- Retries
- Special handling for certain content types
Fix: normalize your flow so all branches follow one read strategy.
Reading both request and response bodies in chained helpers
In middleware-heavy systems, one helper may consume the Request while another consumes the Response. The stack trace can make it look like the response is the only issue.
Fix: audit both request and response stream handling together.
Streamed or large payloads
Cloning can increase memory and processing cost for large bodies. If the payload is big, prefer a single read and rebuild approach instead of cloning repeatedly.
FAQ
Why does this happen only sometimes?
Because different execution paths consume the body at different times. Error handling, logging, retries, or framework internals may only run under certain conditions, making the issue appear intermittent.
Should I always use response.clone()?
No. Use clone() only when you genuinely need two independent readers. If you only need to inspect and return data, reading once and creating a fresh Response is often simpler and safer.
How do I know where the body was first consumed?
Search for all body-reading methods, inspect wrapper utilities, and log response.bodyUsed before suspicious operations. In complex apps, the first consumer is often a helper or middleware rather than the main handler.
The durable fix is simple: consume each response body exactly once, or clone it before any read. Once you make stream ownership explicit, this error stops being random and becomes fully predictable.