Skip to content

fs: fix close listener leak in FileHandle streams - #64227

Open
Y1D7NG wants to merge 2 commits into
nodejs:mainfrom
Y1D7NG:fix-stream-leak
Open

fs: fix close listener leak in FileHandle streams#64227
Y1D7NG wants to merge 2 commits into
nodejs:mainfrom
Y1D7NG:fix-stream-leak

Conversation

@Y1D7NG

@Y1D7NG Y1D7NG commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

fix: #64214

When autoClose is false, FileHandle.createReadStream registers a close listener on the handle. That listener is not removed after the stream finishes normally, so creating streams repeatedly on the same handle accumulates listeners.

In importFd, when autoClose is false, remove that listener and perform the corresponding unref cleanup when the stream ends (on end for read streams, on finish for write streams) or on error.

@nodejs-github-bot nodejs-github-bot added fs Issues and PRs related to the fs subsystem / file system. needs-ci PRs that need a full CI run. labels Jul 1, 2026
@Y1D7NG
Y1D7NG force-pushed the fix-stream-leak branch 5 times, most recently from b433fb3 to c4a165a Compare July 1, 2026 11:11
Fixes: nodejs#64214
Signed-off-by: y1d7ng <y1d7ng@yeah.net>
@Y1D7NG
Y1D7NG force-pushed the fix-stream-leak branch from c4a165a to 96cae83 Compare July 1, 2026 11:18
Comment thread test/parallel/test-fs-promises-file-handle-stream.js Outdated
Fixes: nodejs#64214
Signed-off-by: y1d7ng <y1d7ng@yeah.net>
@Y1D7NG
Y1D7NG requested a review from davidje13 July 1, 2026 16:33
@MikeMcC399 MikeMcC399 added the review wanted PRs that need reviews. label Jul 29, 2026
@MikeMcC399 MikeMcC399 added the request-ci Add this label to start a Jenkins CI on a PR. label Aug 9, 2026
Comment thread lib/internal/fs/streams.js
Comment on lines +159 to +165
function cleanup() {
options.fd.removeListener('close', onclose);
options.fd[kUnref]();
}
stream.once('end', cleanup);
stream.once('finish', cleanup);
stream.once('error', cleanup);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Y1D7NG should we removeListener for all 3 in cleanup, so we don't leave these hanging as well?

Suggested change
function cleanup() {
options.fd.removeListener('close', onclose);
options.fd[kUnref]();
}
stream.once('end', cleanup);
stream.once('finish', cleanup);
stream.once('error', cleanup);
function cleanup() {
options.fd.removeListener('close', onclose);
stream.removeListener('end', cleanup);
stream.removeListener('finish', cleanup);
stream.removeListener('error', cleanup);
options.fd[kUnref]();
}
stream.once('end', cleanup);
stream.once('finish', cleanup);
stream.once('error', cleanup);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I'm aware of for similar functions, it's common practice to (intentionally) leave these listeners dangling on the stream and rely on normal garbage collection to tidy it all up in the end.

There was some discussion on this stuff in #35452. In this case, the duplex stuff doesn't apply so I'd personally say it doesn't need the special handling that was eventually added there.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing that, that's interesting. Unsure how to proceed then. @nodejs/streams is the current behavior as expected?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be clear: in my comment I'm only talking about the events on the stream (end/finish/error) - removing the close listener from the file descriptor (i.e. the purpose of this PR) is definitely necessary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 approvals and a green CI.

Could this PR be landed, or does it need clarification of the above comments first?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comments are non-blocking, although I don't see it hurting waiting extra time to get insights from the right team tho.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the listeners can be left to normal garbage collection

@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 9, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@MikeMcC399 MikeMcC399 removed the review wanted PRs that need reviews. label Aug 10, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fs Issues and PRs related to the fs subsystem / file system. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Close listener leak in fs/promises createReadStream

6 participants