fastify/fastify · error · Error
FST_ERR_REP_RESPONSE_BODY_CONSUMED
FST_ERR_REP_RESPONSE_BODY_CONSUMED
Error message
Response.body is already consumed.
What it means
Thrown in onSendEnd when you reply.send() a WHATWG Response object whose body has already been consumed (response.bodyUsed === true). Fastify uses response.body as the payload stream, so a locked/consumed body cannot be re-read. This protects against double-read of a fetch Response that was already .json()/.text()/.arrayBuffer()'d.
Source
Thrown at lib/reply.js:626
// we need to update the status, add the headers and use it's body as payload
// before continuing
if (payload != null && typeof payload === 'object' && toString.call(payload) === '[object Response]') {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/status
if (typeof payload.status === 'number') {
reply.code(payload.status)
}
// https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
for (const [headerName, headerValue] of payload.headers) {
reply.header(headerName, headerValue)
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Response/body
if (payload.body !== null) {
if (payload.bodyUsed) {
throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
}
}
// Keep going, body is either null or ReadableStream
payload = payload.body
}
const statusCode = res.statusCode
if (payload === undefined || payload === null) {
// according to https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
// we cannot send a content-length for 304 and 204, and all status code
// < 200
// A sender MUST NOT send a Content-Length header field in any message
// that contains a Transfer-Encoding header field.
// For HEAD we don't overwrite the `content-length`
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD' && reply[kReplyTrailers] === null) {
reply[kReplyHeaders]['content-length'] = '0'
}
View on GitHub (pinned to 7299a57d3f)
Solutions
- Do not consume response.body before reply.send() — pass the Response untouched if you only need to forward it.
- If you must read it, reconstruct a new Response: reply.send(new Response(payload, { status, headers })).
- Clone the Response with response.clone() before consuming one copy, and send the other.
Example fix
// before const upstream = await fetch(url) const body = await upstream.json() // body now consumed reply.send(upstream) // after const upstream = await fetch(url) const clone = upstream.clone() const body = await clone.json() reply.send(upstream)
Defensive patterns
Strategy: validation
Validate before calling
function sendableResponse(response) {
return response && !response.bodyUsed
}
if (sendableResponse(upstream)) reply.send(upstream)
else reply.send(new Response(reconstructedBody, { status: upstream.status, headers: upstream.headers })) Type guard
function isResponseBodyAvailable(response) {
return response != null && response.bodyUsed === false
} Try / catch
try {
reply.send(response)
} catch (err) {
if (err.code === 'FST_ERR_REP_RESPONSE_BODY_CONSUMED') {
reply.code(500).send({ error: 'upstream body already consumed' })
} else throw err
} Prevention
- Never call .json()/.text() on a Response you intend to forward — clone it first.
- Forward the original Response untouched when proxying.
- If you must inspect, use response.clone() and consume the clone.
When it happens
Trigger: Calling const data = await res.json() on a fetch Response and then passing the same Response to reply.send(response); or reading response.body with a reader elsewhere before sending it.
Common situations: Logging middleware that inspects a fetched response body for logging and then forwards the Response to the client; proxy handlers that consume the body for transformation but forget to reconstruct a fresh Response.
Related errors
- FST_ERR_REP_READABLE_STREAM_LOCKED
- FST_ERR_SEND_INSIDE_ONERR
- FST_ERR_BAD_TRAILER_NAME
- FST_ERR_BAD_TRAILER_VALUE
- FST_ERR_BAD_STATUS_CODE
AI-assisted analysis of fastify/fastify@7299a57d3f (2026-08-03).
Data as JSON: /data/errors/84b195c528f062b0.json.
Report an issue: GitHub.