denoland/deno · error · TypeError
The body of the Response returned from the serve handler has
Error message
The body of the Response returned from the serve handler has already been consumed
What it means
A Response body can be read exactly once. serve checks responseBodyUsed before sending, and throws this TypeError when the handler returns a Response whose body was already consumed by earlier code, because there is nothing left to put on the wire. The error is routed to onError (default: 500).
Source
Thrown at ext/http/00_serve.ts:747
// The Response prototype check above passes for Response-like objects
// (e.g. a subclass that skipped super(), or a Response from a different
// realm/polyfill). Those don't carry the internal slot we read from
// below, so reject them with a clear error instead of crashing later.
inner = getInnerResponse(response);
if (inner === undefined) {
throw new TypeError(
"Return value from serve handler must be a Response constructed via the Response constructor in this realm",
);
}
if (inner.type === "error") {
throw new TypeError(
"Return value from serve handler must not be an error response (like Response.error())",
);
}
if (responseBodyUsed(response)) {
throw new TypeError(
"The body of the Response returned from the serve handler has already been consumed",
);
}
} catch (error) {
try {
response = await onError(error);
if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
throw new TypeError(
"Return value from onError handler must be a response or a promise resolving to a response",
);
}
inner = toInnerResponse(response);
if (inner === undefined) {
throw new TypeError(
"Return value from onError handler must be a Response constructed via the Response constructor in this realm",
);
}
} catch (error) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Read from a clone when you must inspect: await res.clone().text(), then return the original
- Cache the source data and construct a fresh Response per request
- In middleware, pass res.clone() downstream when you consume one copy
- Check res.bodyUsed before returning and rebuild the Response if it is true
Example fix
// before
Deno.serve(async () => {
const res = Response.json({ ok: true });
console.log(await res.text()); // consumes the body
return res;
});
// after
Deno.serve(async () => {
const res = Response.json({ ok: true });
console.log(await res.clone().text()); // read the clone
return res;
}); Defensive patterns
Strategy: validation
Validate before calling
// Tripwire before returning from the handler
if (response instanceof Response && response.bodyUsed) {
throw new Error("Response body was consumed before returning");
}
return response; Type guard
function isUnconsumedResponse(v) {
return v instanceof Response && !v.bodyUsed;
} Try / catch
try {
return await handler(req);
} catch (err) {
if (err instanceof TypeError && err.message.includes("already been consumed")) {
return new Response("Response body already consumed", { status: 500 });
}
throw err;
} Prevention
- Inspect bodies via clone() and return the unread original
- Never cache Response instances across requests; cache the payload data
- Check bodyUsed on responses that passed through middleware
When it happens
Trigger: Calling res.text()/res.json()/res.arrayBuffer() on the response before returning it; caching a single Response object and returning it for multiple requests; middleware that inspects a response body without cloning it.
Common situations: Logging response payloads (await res.text() then return res); singleton pre-built responses reused across requests; test code that verifies the body of the very object it returns.
Related errors
- Return value from serve handler must be a response or a prom
- Return value from serve handler must be a Response construct
- Return value from serve handler must not be an error respons
- Response with null body status cannot have body
- Body is unusable
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/9eb19c85196fef12.
Report an issue: GitHub.