denoland/deno · error · TypeError
The body of the Response returned from the serve handler…
Error message
The body of the Response returned from the serve handler has already been consumed
What it means
Before serving, Deno.serve checks responseBodyUsed() and refuses a Response whose body stream has already been read or cancelled. Once a body is consumed it cannot be transmitted again, so the handler is returning an empty shell. The throw is routed to onError (500 by default).
Solutions
- Replace cached Response constants with factories: `const notFound = () => new Response('Not Found', { status: 404 })`
- Log via a clone: `await response.clone().text()`, return the original
- If you must read the body, rebuild the Response: `const t = await r.text(); return new Response(t, r)`
- Never hand the same Response object to two requests
Example fix
// before
const NOT_FOUND = new Response('Not Found', { status: 404 });
Deno.serve(() => NOT_FOUND); // second request throws: body already consumed
// after
const notFound = () => new Response('Not Found', { status: 404 });
Deno.serve(() => notFound()); // fresh Response every request Defensive patterns
Strategy: validation
Validate before calling
// Validate before returning, and never reuse consumed Responses:
if (response.bodyUsed) {
throw new Error('handler consumed the response body before returning it');
}
return response; Try / catch
// Logging without consuming: read the clone, return the original const copy = response.clone(); await logBody(await copy.text()); return response;
Prevention
- Replace module-level cached Response constants with factory functions
- Use clone() before any read meant for logging or auditing
- If a body must be read then served, rebuild: new Response(await r.text(), r)
When it happens
Trigger: Reading the body for logging/inspection (`await response.text()`) and then returning the same object, returning a module-level cached Response instance that was already served once, or middleware that inspects the body without cloning.
Common situations: Response-logging middleware, a shared `const NOT_FOUND = new Response('Not Found', { status: 404 })` constant returned from many requests (the first serve consumes it), teeing bodies for audit trails without using clone().
Related errors
- Invalid response
- Body is unusable
- Both 'cert' and 'key' must be provided to enable HTTPS
- Cannot serve HTTP requests: either a `handler` or `options`…
- Cannot serve HTTP requests: handler must be a function…
AI-assisted analysis of denoland/deno@abd22074e4 (2026-08-20).
Data as JSON: /api/errors/9eb19c85196fef12.
Report an issue: GitHub.
Appendix: source
Thrown at ext/http/00_serve.ts:766
// 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 abd22074e4)