denoland/deno · error · TypeError
Body is unusable
Error message
Body is unusable
What it means
Request.prototype.clone() checks this[_body].unusable() first: if the body stream is already disturbed or locked, clone() throws TypeError 'Body is unusable'. Cloning must tee the underlying stream, which is only possible before anything has read it. The message has the prefix 'Failed to execute Request.clone' in the error cause chain of the outer DOMException.
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 89f33cbef2)
Solutions
- Clone before first read: const copy = req.clone(); then read one of them
- If already read, reconstruct a new Request from the buffered body string/bytes instead of cloning
- Read via req.blob()/text() once and pass the materialized value to all consumers
Example fix
// before const data = await req.text(); const copy = req.clone(); // throws: body disturbed // after const copy = req.clone(); // tee while unused const data = await req.text(); // copy still has an independent unread body
Defensive patterns
Strategy: validation
Validate before calling
function cloneOrRebuild(req, bodyTextIfRead) {
try {
return req.clone();
} catch {
return new Request(req.url, {
method: req.method,
headers: req.headers,
body: bodyTextIfRead,
});
}
} Try / catch
try {
copy = req.clone();
} catch (err) {
if (err instanceof TypeError && /Body is unusable|Failed to execute 'Request.clone'/.test(String(err.cause ?? err.message))) {
copy = new Request(req.url, { method: req.method, headers: req.headers, body: buffered });
} else throw err;
} Prevention
- Clone before the first body read, always
- Keep a buffered copy (text/bytes) when a request body must be reused
- Order handler code so clone() precedes any await that may consume the body
When it happens
Trigger: const r = new Request(url, { method: 'POST', body: 'x' }); await r.text(); r.clone() — any read, iteration, or pipe of the body before clone().
Common situations: Retry/middleware code that clones a request after logging its body; clone-then-read ordering bugs in async handlers where an earlier await consumed the stream.
Related errors
- Input request's body is unusable
- Body is unusable
- Request with GET/HEAD method cannot have body
- Response body is already used
- Invalid header: length must be 2, but is ${header.length}
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/e5276ff0c31662c8.
Report an issue: GitHub.