fix: guard PersistentFile.write() against a destroyed write stream - #1121
Open
raphyabak wants to merge 1 commit into
Open
fix: guard PersistentFile.write() against a destroyed write stream#1121raphyabak wants to merge 1 commit into
raphyabak wants to merge 1 commit into
Conversation
Fixes node-formidable#958. PersistentFile.write() only checked this._writeStream.closed before writing, unlike VolatileFile.write() which already checks both .closed and .destroyed. When a request is aborted mid-upload, destroy() calls this._writeStream.destroy(), which flips .destroyed synchronously but only flips .closed later, once the underlying fd finishes closing. A write() call landing in that window bypassed the guard and reached the real fs write stream, which throws ERR_STREAM_DESTROYED ("Cannot call write after a stream was destroyed") outside of any callback formidable can catch, crashing the process. Add the same .destroyed check PersistentFile's sibling class already has.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #958.
PersistentFile.write()only checkedthis._writeStream.closedbefore writing:VolatileFile.write()(the sibling class) already checks both:When a request is aborted mid-upload,
destroy()callsthis._writeStream.destroy(), which flips.destroyedsynchronously — but.closedonly flips later, once the underlying fd actually finishes closing. Awrite()call landing in that window skips the (incomplete) guard and reaches the realfswrite stream, which throwsERR_STREAM_DESTROYED("Cannot call write after a stream was destroyed") outside of any callback formidable can catch, crashing the process. This matches the exact stack trace and crash pattern in the issue, and was correctly diagnosed there by @tommyhtran, comparing the two classes'write()methods.Fix
Add the same
.destroyedcheckVolatileFilealready has.Test plan
test/unit/persistent-file-write.test.jscoveringwrite()with a mock stream in three states: normal,closed, anddestroyed-but-not-yet-closed(the state that reproduces the crash).writewas called when it shouldn't have been) — not a flaky timeout, since the mock stream'swritealways resolves its callback regardless of whether the guard should have short-circuited first.pnpm test-jest— 15 suites, 94 passing, 3 pre-existing skips (unrelated).eslintpasses on both changed files (matches this repo's existing lint baseline — the one remaining@jest/globalsresolution warning already exists on the pre-existingvolatile-file.test.jstoo, unrelated to this change).Greptile Summary
This PR prevents
PersistentFile.write()from writing to a destroyed stream during upload cleanup, matching the existingVolatileFilelifecycle guard.Confidence Score: 5/5
The PR appears safe to merge, with the destroyed-stream guard addressing the abort-time write failure without introducing an established regression.
The production change narrowly aligns persistent-file behavior with the sibling volatile-file implementation, and the tests exercise the relevant active, closed, and destroyed stream states.
Important Files Changed
Reviews (1): Last reviewed commit: "fix: guard PersistentFile.write() agains..." | Re-trigger Greptile
Context used: