denoland/deno · error · TypeError
Body already consumed.
Error message
Body already consumed.
What it means
Thrown by the body mixin's textStream() accessor (ext/fetch/22_body.js) when the inner body exists and inner.unusable() is true — the same consumed/locked condition as consume(), but surfaced through the property-like textStream API. Note the trailing period in the message; it comes from the newer property-based body accessors.
Solutions
- Choose one consumption strategy per body: either one-shot (text/json) or streaming (textStream/body), never both.
- If you must re-stream, capture the original bytes first and construct a new Request/Response with them.
- Clone the request before the first read if a second consumer might need the stream.
Example fix
// before
const raw = await req.text();
for await (const chunk of req.textStream) { ... } // Body already consumed.
// after
for await (const chunk of req.textStream) {
process(chunk);
}
// if you also need the full text, accumulate chunks inside the loop Defensive patterns
Strategy: validation
Validate before calling
const canStream = req.body !== null && !req.bodyUsed && !req.body?.locked;
if (!canStream) throw new Error("body unavailable for streaming");
for await (const chunk of req.textStream) { /* ... */ } Type guard
function isBodyStreamable(r: Request | Response): boolean {
return !r.bodyUsed && (r.body === null || !r.body.locked);
} Prevention
- Pick one read strategy (one-shot vs streaming) per request.
- Check bodyUsed before accessing stream APIs.
- Clone before reading if you might need the other representation.
When it happens
Trigger: Accessing `request.textStream` (or the property path that calls it) after the body was already read via text()/json()/arrayBuffer() or after res.body was locked by a reader.
Common situations: Switching mid-handler from one-shot reads to streaming; frameworks that call textStream internally after middleware consumed the body; debugging code that reads the body first and later pipes textStream.
Related errors
- Body already consumed
- Body is unusable
- Body is unusable
- Input request's body is unusable
- ERR_STREAM_NULL_VALUES
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/49b40fc44fc8d553.
Report an issue: GitHub.
Appendix: source
Thrown at ext/fetch/22_body.js:414
},
text: {
__proto__: null,
/** @returns {Promise<string>} */
value: function text() {
return consumeBody(this, "text");
},
writable: true,
configurable: true,
enumerable: true,
},
textStream: {
__proto__: null,
/** @returns {ReadableStream<string>} */
value: function textStream() {
webidl.assertBranded(this, prototype);
const inner = this[bodySymbol];
if (inner !== null && inner.unusable()) {
throw new TypeError("Body already consumed.");
}
if (inner === null) {
// A null body yields an empty, already-closed stream. Per the spec
// this is returned as-is; no decoder is set up for it.
const emptyStream = new ReadableStream();
readableStreamClose(emptyStream);
return emptyStream;
}
return inner.stream.pipeThrough(new TextDecoderStream());
},
writable: true,
configurable: true,
enumerable: true,
},
};
return ObjectDefineProperties(prototype, mixin);
}
View on GitHub (pinned to 9ad36f7a2c)