Skip to content

Sending the same JavaScript Response twice can crash the server #5919

@jadenPete

Description

@jadenPete

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

5.2.0

Plugin version

No response

Node.js version

18.19.1

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

Ubuntu 24.04.1

Description

With #5286, request handlers in Fastify can return JavaScript Responses, whose response bodies are backed by ReadableStreams. If the same response is sent multiple times instead of a fresh one for each request, it's possible for Fastify to crash when converting the ReadableStream underlying the response into a Readable. This is done here:

const nodeStream = Readable.fromWeb(payload)

One can reproduce this error by running the following server and issuing a GET request to /:

const fastify = require('fastify');
const app = fastify({
	logger: true,
});

const stream = new ReadableStream();
const response = new Response(stream);

stream.getReader();

app.get('/', (request, reply) => {
	reply.send(response);
});

app.addHook('onSend', async () => {});
app.listen({
	port: 8000,
});
{"level":30,"time":1735846078242,"pid":130836,"hostname":"???","msg":"Server listening at http://127.0.0.1:8000"}
{"level":30,"time":1735846082740,"pid":130836,"hostname":"???","reqId":"req-1","req":{"method":"GET","url":"/locked","host":"localhost:8000","remoteAddress":"127.0.0.1","remotePort":43396},"msg":"incoming request"}
onSend has been triggered.
node:internal/errors:496
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_INVALID_STATE]: Invalid state: ReadableStream is locked
    at new NodeError (node:internal/errors:405:5)
    at setupReadableStreamDefaultReader (node:internal/webstreams/readablestream:2153:11)
    at new ReadableStreamDefaultReader (node:internal/webstreams/readablestream:829:5)
    at ReadableStream.getReader (node:internal/webstreams/readablestream:350:14)
    at Object.newStreamReadableFromReadableStream (node:internal/webstreams/adapters:500:33)
    at Readable.fromWeb (node:internal/streams/readable:1444:27)
    at sendWebStream (/home/jpeterson/Downloads/fastify-test/node_modules/fastify/lib/reply.js:673:31)
    at onSendEnd (/home/jpeterson/Downloads/fastify-test/node_modules/fastify/lib/reply.js:636:5)
    at wrapOnSendEnd (/home/jpeterson/Downloads/fastify-test/node_modules/fastify/lib/reply.js:536:5)
    at next (/home/jpeterson/Downloads/fastify-test/node_modules/fastify/lib/hooks.js:292:7) {
  code: 'ERR_INVALID_STATE'
}

Node.js v18.19.1

I think a good fix would be to wrap the Readable.fromWeb call in a try/catch block, but there may be a better place higher up in the call stack to wrap to prevent other errors from crashing the server.

Link to code that reproduces the bug

No response

Expected Behavior

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions