denoland/deno · error · TypeError
Body already consumed
Error message
Body already consumed
What it means
Thrown by InnerBody.consume() in ext/fetch/22_body.js when the body is unusable — the stream is locked or disturbed, or the static body was already consumed. This backs every one-shot body reader: request.json(), request.text(), request.arrayBuffer(), response.formData(), etc. A body can only be consumed once.
Solutions
- Read the body once, then reuse the parsed value: `const data = await req.json().catch(() => null)` and pass `data` downstream.
- When multiple consumers are needed, clone first: `const [a, b] = [req.clone(), req]` (clone before any read).
- In middleware, buffer the body (await once) and reconstruct a new Request with the buffered bytes for downstream handlers.
Example fix
// before console.log(await req.text()); const json = await req.json(); // Body already consumed // after const raw = await req.text(); const json = JSON.parse(raw);
Defensive patterns
Strategy: validation
Validate before calling
function bodyReadable(b) {
const s = b.body;
return s !== null && !(s.locked);
}
if (!bodyReadable(req)) throw new Error("body already used; buffer and pass the parsed value instead"); Type guard
function hasUnreadBody(r: Request | Response): boolean {
return r.body === null || !(r.body.locked); // disturbed streams are not directly detectable; track reads yourself
} Try / catch
let data; try { data = await req.json(); } catch (e) { if (e instanceof TypeError && e.message === "Body already consumed") { /* re-fetch or use cached value */ } else throw e; } Prevention
- Read each body exactly once; pass the parsed value downstream.
- Use req.clone() before the first read when two consumers need the body.
- In middleware, buffer the body and rebuild the Request for the handler.
When it happens
Trigger: Calling two one-shot readers on the same body: `(await req.text())` followed by `req.json()`; reading `res.body` with a reader and then calling `res.text()`; cloning after consumption; middleware that logs the body and then the handler reads it again.
Common situations: Logging middleware that calls request.json() before the route handler; error handlers that try to re-read the body after a failed parse; double-calling formData() in upload handlers; using the same Request object for fetch() after reading its body.
Related errors
- Body already consumed.
- Body is unusable
- Body is unusable
- Input request's body is unusable
- ReadableStream is locked or disturbed
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/abe3287dd6ef37fc.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/22_body.js:189
*/
consumed() {
if (
ObjectPrototypeIsPrototypeOf(
ReadableStreamPrototype,
this.streamOrStatic,
)
) {
return isReadableStreamDisturbed(this.streamOrStatic);
}
return this.streamOrStatic.consumed;
}
/**
* https://fetch.spec.whatwg.org/#concept-body-consume-body
* @returns {Promise<Uint8Array>}
*/
consume() {
if (this.unusable()) throw new TypeError("Body already consumed");
if (
ObjectPrototypeIsPrototypeOf(
ReadableStreamPrototype,
this.streamOrStatic,
)
) {
readableStreamThrowIfErrored(this.stream);
return PromisePrototypeCatch(
readableStreamCollectIntoUint8Array(this.stream),
(e) => {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, e)) {
// TODO(kt3k): We probably like to pass e as `cause` if BadResource supports it.
throw new e.constructor(
"Cannot read body as underlying resource unavailable",
);
}
throw e;
},View on GitHub (pinned to 9ad36f7a2c)