denoland/deno · error · TypeError
Body already consumed
Error message
Body already consumed
What it means
InnerBody.consume() in ext/fetch/22_body.js backs res.text(), res.json(), res.arrayBuffer(), res.blob(), res.bytes() and res.formData(). It throws TypeError "Body already consumed" when unusable(): the underlying ReadableStream is locked or disturbed (a reader was acquired, or it was piped/read), or a static body is already marked consumed. A body can be consumed exactly once.
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 89f33cbef2)
Solutions
- Check res.bodyUsed before any read and parse once, storing the result
- Call res.clone() before the first read when two consumers need the body
- In middleware, buffer the bytes: const buf = await res.clone().arrayBuffer() and pass the original on, or construct a fresh Response from the buffered bytes
Example fix
// before const a = await res.json(); const b = await res.json(); // TypeError: Body already consumed // after const data = await res.json(); const a = data; const b = data; // or: const [a, b] = await Promise.all([res.clone().json(), res.json()]);
Defensive patterns
Strategy: validation
Validate before calling
if (res.bodyUsed) {
throw new Error("body already read - reuse the cached parsed value");
}
const data = await res.json(); Type guard
function hasUnreadBody(res: Response | Request): boolean {
return !res.bodyUsed && !(res.body?.locked ?? false);
} Try / catch
try { return await res.json(); } catch (e) {
if (e instanceof TypeError && e.message === "Body already consumed") {
throw new Error("duplicate body read; cache the first parse or clone() before reading");
}
throw e;
} Prevention
- Parse a body once and pass the parsed value downstream
- Call res.clone() before the first read when logging middleware also needs the body
- Store parsed data (not the Response) in caches
When it happens
Trigger: await res.text() twice on the same response; res.json() after const reader = res.body.getReader(); calling res.formData() after piping res.body elsewhere.
Common situations: Logging middleware that reads the body for logging and then passing the same response to business code; retry logic that re-parses a response; caching layers that hold the Response object instead of its parsed content.
Related errors
- Body already consumed.
- ReadableStream is locked or disturbed
- Request with GET/HEAD method cannot have body
- Input request's body is unusable
- Body is unusable
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/abe3287dd6ef37fc.
Report an issue: GitHub.