fix: stop block reassembly on premature EOF - #151
Draft
robjarawan wants to merge 2 commits into
Draft
Conversation
Test Results376 tests 375 ✅ 1m 44s ⏱️ Results for commit bee6465. |
This was referenced Sep 6, 2026
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.
Closes #65
What happened
Block_reassembly.after_work()kept reading until it reached the manifest byte count. If the part reached EOF first,read()returnedb'', the byte counter stopped moving, and the loop heldresult.flufl_lockforever.I reproduced it through a real local
subscribeflow using the normalresult§block_0000,4bytes_§suffix. The source and announcement both had four bytes, then a proof callback truncated the downloaded part to two bytes immediately before reassembly.Before this change:
{"hung": true, "lock_available": false, "part_exists": true, "retry_records": 0, "returncode": -9, "root_size": 0}The complete four-byte control returned normally and produced the correct file.
What I changed
I changed the copy loop to detect EOF before the announced block size. It now closes both files, keeps the part, moves the message to
worklist.failed, releases the reassembly lock and continues.After the change, the same running-flow proof reports:
{"hung": false, "lock_available": true, "part_exists": true, "retry_records": 1, "returncode": 0, "root_size": 2}The unit regression then repairs the retained part to four bytes and retries it. Reassembly produces the exact
abcdresult and releases the lock.Validation
compileallandgit diff --check: passedA naturally short source is caught by
Flow.download()before this callback. The confirmed trigger is the part becoming short after download, including a filesystem race or an earlier callback.The running proof also reaches the separate retry-path ordering problem in #66: the failed record is preserved, but its immediate retry uses an adjusted path. I left that out of this change so this PR only removes the infinite loop and preserves recovery state.
CI scope
This branch includes the existing local-fixture workflow repair from #140 so fork CI does not run public dynamic flows.