denoland/deno · error · TypeError
Body is unusable
Error message
Body is unusable
What it means
Request.prototype.clone() must tee the body stream so both copies can be read independently. If this request's body is already disturbed (read via text()/json()/stream reader) or locked (piped/stream-locked), teeing is impossible and clone() throws TypeError "Body is unusable". Bodies are single-use; clone only works on a request whose body has never been touched.
Solutions
- Clone first, then read: const dup = r.clone() before any body access, and read from the duplicate
- Buffer the body up front (const body = await r.text()) and build two new Requests from the string
- For file uploads you cannot buffer, pass the underlying Blob/File as body — it is re-readable and clone-safe
Example fix
// before
const r = new Request(url, { method: "POST", body: "x" });
await r.text();
r.clone(); // throws: Body is unusable
// after
const r = new Request(url, { method: "POST", body: "x" });
const dup = r.clone(); // clone before any read
await r.text();
// dup is still fully readable Defensive patterns
Strategy: validation
Validate before calling
if (req.bodyUsed) {
throw new Error("Cannot clone: body already consumed — clone before reading");
}
const dup = req.clone(); Type guard
function isCloneableRequest(req: Request): boolean {
return !req.bodyUsed;
} Try / catch
try {
return req.clone();
} catch (err) {
if (err instanceof TypeError && err.message === "Body is unusable") {
// reconstruct from buffered payload instead of cloning
return new Request(req.url, { method: req.method, headers: [...req.headers], body: buffered });
}
throw err;
} Prevention
- Clone first, read second — make it a habit in log-and-forward middleware
- Use re-readable body sources (Blob/File/Uint8Array) when duplicates are needed
- Await any body read only after you have made all clones you need
When it happens
Trigger: const r = new Request(url, { method: "POST", body: "x" }); await r.text(); r.clone(); also r.clone() while the body is being piped to another stream.
Common situations: Clone-after-log patterns (read body for logging, then clone to forward); retry wrappers cloning a request after a failed attempt that already consumed the body; duplicate-processing pipelines.
Related errors
- Body is unusable
- Input request's body is unusable
- Body already consumed
- Body already consumed.
- Request with GET/HEAD method cannot have body
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e5276ff0c31662c8.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/23_request.js:674
}
return referrer;
}
get referrerPolicy() {
webidl.assertBranded(this, RequestPrototype);
return this[_request].referrerPolicy ?? "";
}
get signal() {
webidl.assertBranded(this, RequestPrototype);
return this[_signal];
}
clone() {
const prefix = "Failed to execute 'Request.clone'";
webidl.assertBranded(this, RequestPrototype);
if (this[_body] && this[_body].unusable()) {
throw new TypeError("Body is unusable");
}
const clonedReq = cloneInnerRequest(this[_request]);
const materializedSignal = this[_signal];
const clonedSignal = createDependentAbortSignal(
[materializedSignal],
prefix,
);
const request = new Request(_brand);
request[_request] = clonedReq;
request[_signalCache] = clonedSignal;
headerListFromHeaders(this[_headers]);
request[_headersGuard] = guardFromHeaders(this[_headers]);
return request;
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {View on GitHub (pinned to 9ad36f7a2c)