Conversation
…d parts The request 'close' event fires once the message body has fully drained into busboy's writable buffer, which can happen while the consumer is still reading an earlier file part and busboy is still holding parsed but undelivered parts. The cleanup bound to that event pushed the end marker into the same queue the parts flow through, so trailing parts were silently lost. - end iteration on busboy's own end events; request 'close' only ends it when the message did not complete (client abort), and then with a new PrematureCloseError instead of a silent clean end - surface busboy's 'Unexpected end of multipart data' as an error so truncated multipart data is never mistaken for a complete iteration; a fully empty body is still treated as an empty form - end iteration when the consumer destroys a file stream without reading it to the end, since busboy will never emit 'finish' then Fixes #630 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 #630.
request.on('close')fires once the message body has fully drained into busboy's writable buffer. Busboy defers its write ack when a file stream backs up, so on a fast/re-segmented connection the request can complete while busboy still holds parsed-but-undelivered parts. Thecleanupbound toclosepushed the end marker into the same queue the parts flow through, and every part emitted afterwards landed behind it — trailing file parts and fields were silently lost, exactly as reported.Changes
request.on('close')only terminates the iteration when the message did not complete (readableEnded === false, i.e. a client abort), and then with a newPrematureCloseError(FST_MP_PREMATURE_CLOSE, exposed viafastify.multipartErrorsand the TypeScript types) instead of a silent clean end.Unexpected end of multipart dataas an error. The suppression came in mechanically with the busboy v2.1.0 upgrade (use @fastify/busboy v2.1.0 #506); it made truncated uploads indistinguishable from complete ones. A fully empty body (zero bytes received) is still treated as an empty form, preserving the behavior pinned bymultipart-empty-body.test.js.finishin that case; previously theclose-based cleanup masked this, and without a guardparts()would hang forever (caught by the existing should not freeze when error is thrown during processing test).Reproduction / tests
Written TDD-style in
test/fix-630.test.js(red onmain, green here):close— slow consumer, 200 KB file + 6 KB mask + three fields. A large busboyhighWaterMarkmakes the failing event ordering deterministic on loopback (the same ordering the issue reproduced through Docker Desktop's port forwarding with default settings); onmainthe handler sees only the first file.Unexpected end of multipart datainstead of ending cleanly.close-based cleanup doesn't reintroduce hangs on aborts.Behavioral note
Consumers that previously saw a clean-looking, silently-truncated iteration on truncated or aborted uploads now get an error — that is the point of the fix, but it may warrant a semver-major release.
Checklist
npm run test— 90/90 unit tests, 100 % coverage, tstyche 34/34multipartErrorsmechanism)🤖 Generated with Claude Code