denoland/deno · error · TypeError
Input request's body is unusable
Error message
Input request's body is unusable
What it means
Request constructor step 41: when no new body is supplied in init but the input Request has one, the input's body must be teed via createProxy(). A body stream that is already disturbed or locked (previously read, or currently being read) is unusable, and re-using it throws TypeError "Input request's body is unusable". A request's body can only be consumed once.
Solutions
- Call input.clone() BEFORE reading the body, then build the new Request from the clone
- Read the body once into memory (await req.text()) and construct fresh Requests with that string as body
- Never re-use a request object after awaiting any body-reading method (text/json/arrayBuffer/formData)
Example fix
// before
const src = new Request(url, { method: "POST", body: payload });
await src.text(); // log the body
const fwd = new Request(src); // throws: body unusable
// after
const src = new Request(url, { method: "POST", body: payload });
const copy = src.clone(); // tee before reading
await src.text(); // log the body
const fwd = new Request(copy); // ok Defensive patterns
Strategy: validation
Validate before calling
if (input instanceof Request && input.bodyUsed) {
throw new Error("Input request body already consumed — pass the payload string instead");
}
const req = new Request(input); Type guard
function isReusableRequestBody(req: Request): boolean {
return !req.bodyUsed && req.body !== null ? !req.body.locked : true;
} Try / catch
try {
const fwd = new Request(input);
} catch (err) {
if (err instanceof TypeError && err.message.includes("body is unusable")) {
// body already read: build a fresh request from the buffered payload
new Request(url, { method, headers, body: bufferedPayload });
} else throw err;
} Prevention
- Clone a request before reading its body if you need to forward it later
- Track bodyUsed on requests you pass around; treat true as single-use-consumed
- Buffer small bodies (text) once and rebuild requests from the string
When it happens
Trigger: const r = new Request(url, { method: "POST", body: data }); await r.text(); new Request(r); also re-using a request whose stream is mid-read (piped to a WritableStream) as input to a new Request.
Common situations: Peeking at a body (logging/parsing) then trying to forward the same request object; retry logic that re-wraps an already-read request; middleware that reads the body and later constructs a new Request from the original.
Related errors
- Body is unusable
- Body already consumed
- Body already consumed.
- Body is unusable
- Request with GET/HEAD method cannot have body
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/addf82a81f562d19.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/23_request.js:585
// 37.
if (init.body !== undefined && init.body !== null) {
const res = extractBody(init.body);
initBody = res.body;
if (res.contentType !== null && !this[_headers].has("content-type")) {
this[_headers].append("Content-Type", res.contentType);
}
}
// 38.
const inputOrInitBody = initBody ?? inputBody;
// 40.
let finalBody = inputOrInitBody;
// 41.
if (initBody === null && inputBody !== null) {
if (input[_body] && input[_body].unusable()) {
throw new TypeError("Input request's body is unusable");
}
finalBody = inputBody.createProxy();
}
// 42.
request.body = finalBody;
}
get method() {
webidl.assertBranded(this, RequestPrototype);
if (this[_method]) {
return this[_method];
}
this[_method] = this[_request].method;
return this[_method];
}
get url() {View on GitHub (pinned to 9ad36f7a2c)