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 init.body is absent but the input Request has a body, the input body is proxied into the new request — but only if it is still usable. input[_body].unusable() returns true once the stream is disturbed or locked (already read, tee'd, or piped), and then TypeError 'Input request's body is unusable' is thrown.
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 89f33cbef2)
Solutions
- Read the body once into memory and rebuild: new Request(url, { method, headers, body: await input.text() })
- Clone BEFORE reading: const copy = input.clone(); const text = await input.text(); — then reuse copy
- In middleware, buffer the body first and pass the buffer to downstream handlers
Example fix
// before
const bodyText = await input.text(); // input now unusable
const forwarded = new Request(target, input); // TypeError
// after
const bodyText = await input.text();
const forwarded = new Request(target, {
method: input.method,
headers: input.headers,
body: bodyText,
}); Defensive patterns
Strategy: validation
Validate before calling
async function rewrapRequest(input, targetUrl) {
// Buffer once; safe even if already partially handled upstream
const bodyText = await input.clone().text(); // clone guards against double-read below
return new Request(targetUrl, {
method: input.method,
headers: input.headers,
body: ['GET', 'HEAD'].includes(input.method) ? undefined : bodyText,
});
} Try / catch
try {
derived = new Request(target, input);
} catch (err) {
if (err instanceof TypeError && err.message.includes('body is unusable')) {
derived = new Request(target, { method: input.method, headers: input.headers, body: bufferedBody });
} else throw err;
} Prevention
- Clone an input Request before reading its body, then derive from the clone
- Buffer bodies in middleware and forward the buffered value
- Never construct derived Requests from a body you have already consumed
When it happens
Trigger: const reqB = new Request(reqA) after reqA's body was already read (reqA.text(), reqA.json(), iterated, or piped through a stream).
Common situations: Middleware that inspects a body (await req.text()) and then constructs a derived Request for forwarding; logging proxies that consume and re-wrap requests; retry wrappers around an already-read request.
Related errors
- Body is unusable
- Request with GET/HEAD method cannot have body
- Body is unusable
- 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/addf82a81f562d19.
Report an issue: GitHub.