remix-run/react-router · error · Error
Failed to clone server response
Error message
Failed to clone server response
What it means
Before building the HTML response, the RSC SSR server clones the handler's `Response` (`detectRedirectResponse`) and decodes that clone to detect redirects and errors thrown during render. If the clone has no body, the original response's stream was already used or disturbed — `Response.clone()` of a consumed stream yields an empty body — and this error throws. It is the classic Web Streams footfall: a response body can only be read once.
Source
Thrown at packages/react-router/lib/rsc/server.ssr.tsx:198
deepestRenderedBoundaryId = boundaryId;
},
},
formState: {
get() {
return payloadPromise.then((payload) =>
payload.type === "render" ? payload.formState : undefined,
);
},
},
}) as DecodedPayload;
};
let renderRedirect: { status: number; location: string } | undefined;
let renderError: unknown;
try {
if (!detectRedirectResponse.body) {
throw new Error("Failed to clone server response");
}
const payload = (await createFromReadableStream(
detectRedirectResponse.body,
)) as RSCPayload;
if (
serverResponse.status === SINGLE_FETCH_REDIRECT_STATUS &&
payload.type === "redirect"
) {
if (hasInvalidProtocol(payload.location)) {
throw new Error("Invalid redirect location");
}
const headers = new Headers(serverResponse.headers);
headers.delete("Content-Encoding");
headers.delete("Content-Length");
headers.delete("Content-Type");
headers.delete("X-Remix-Response");
headers.set("Location", payload.location);View on GitHub (pinned to 7aea711dd1)
Solutions
- Clone before you read: `const clone = response.clone(); await clone.text(); return response;` — never return the instance you read from.
- Or reconstruct: read the body once and return `new Response(text, response)` with the original status/headers.
- Remove body-reading debug logs from middleware in production paths.
- If using an adapter, upgrade it — older adapters consumed the stream before the RSC pipeline could clone it.
Example fix
// before (middleware consumes the body) const body = await response.text(); logger.debug(body); return response; // after (clone before reading) const clone = response.clone(); logger.debug(await clone.text()); return response;
Defensive patterns
Strategy: validation
Validate before calling
// middleware that must inspect the body: clone first const inspect = response.clone(); const text = await inspect.text(); log(text); return response; // original still readable
Type guard
const isReadableResponse = (res: Response): boolean => res.body != null && !res.body.locked;
Prevention
- Clone a Response before reading it in middleware; never return the instance you consumed.
- Or read once and rebuild: return new Response(text, response).
- Remove body-logging from production middleware paths; Web streams are single-read.
When it happens
Trigger: Custom middleware or `handleRequest` code calling `await response.text()`/`.json()` and then returning the same Response; passing a Response whose body lock was released via `body.cancel()`; adapters that tee/read the stream before handing it back to the framework.
Common situations: Logging response bodies in middleware during debugging; auth middleware inspecting responses before returning them; wrapping the framework handler with timing/audit layers that consume the body.
Related errors
- Missing body in server response
- Prerender (data): Received a ${response.status} status code
- Invalid redirect location
- You cannot call `runClientMiddleware()` from a static handle
- The "@vitejs/plugin-rsc" plugin should be placed after the R
AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18).
Data as JSON: /api/errors/9e5bcb10d395d56b.
Report an issue: GitHub.