{"record":{"id":"9e5bcb10d395d56b","repo":"remix-run/react-router","slug":"failed-to-clone-server-response","errorCode":null,"errorMessage":"Failed to clone server response","messagePattern":"Failed to clone server response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router/lib/rsc/server.ssr.tsx","lineNumber":198,"sourceCode":"          deepestRenderedBoundaryId = boundaryId;\n        },\n      },\n      formState: {\n        get() {\n          return payloadPromise.then((payload) =>\n            payload.type === \"render\" ? payload.formState : undefined,\n          );\n        },\n      },\n    }) as DecodedPayload;\n  };\n\n  let renderRedirect: { status: number; location: string } | undefined;\n  let renderError: unknown;\n\n  try {\n    if (!detectRedirectResponse.body) {\n      throw new Error(\"Failed to clone server response\");\n    }\n    const payload = (await createFromReadableStream(\n      detectRedirectResponse.body,\n    )) as RSCPayload;\n    if (\n      serverResponse.status === SINGLE_FETCH_REDIRECT_STATUS &&\n      payload.type === \"redirect\"\n    ) {\n      if (hasInvalidProtocol(payload.location)) {\n        throw new Error(\"Invalid redirect location\");\n      }\n\n      const headers = new Headers(serverResponse.headers);\n      headers.delete(\"Content-Encoding\");\n      headers.delete(\"Content-Length\");\n      headers.delete(\"Content-Type\");\n      headers.delete(\"X-Remix-Response\");\n      headers.set(\"Location\", payload.location);","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/remix-run/react-router/blob/7aea711dd1ae2bc5a076d13ff17291829690fa74/packages/react-router/lib/rsc/server.ssr.tsx#L180-L216","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (middleware consumes the body)\nconst body = await response.text();\nlogger.debug(body);\nreturn response;\n\n// after (clone before reading)\nconst clone = response.clone();\nlogger.debug(await clone.text());\nreturn response;","handlingStrategy":"validation","validationCode":"// middleware that must inspect the body: clone first\nconst inspect = response.clone();\nconst text = await inspect.text();\nlog(text);\nreturn response; // original still readable","typeGuard":"const isReadableResponse = (res: Response): boolean =>\n  res.body != null && !res.body.locked;","tryCatchPattern":null,"preventionTips":["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."],"tags":["rsc","ssr","response-clone","stream-consumed","middleware"],"backgroundTag":"response-body-already-consumed","analyzedSha":"7aea711dd1ae2bc5a076d13ff17291829690fa74","analyzedAt":"2026-08-18T18:04:14.938Z","contentChangedAt":"2026-08-18T18:04:14.938Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}