-
Notifications
You must be signed in to change notification settings - Fork 237
Fix silent failure receiving files: read large frames in bounded chunks - #225
Fix silent failure receiving files: read large frames in bounded chunks #225aryzae wants to merge 2 commits into
Conversation
grishka
commented
Sep 2, 2026
From what I gather, this was never an issue to begin with.
Did you use AI to generate this?
aryzae
commented
Sep 3, 2026
You're right that this isn't a general problem. I've since tried the same transfers on a second Mac (my personal one, stock 2.2.0), and everything arrives fine there.
What I left out of the description: the Mac where this fails every time is a managed work machine with a third-party Endpoint Security extension that includes a network content filter (the connection logs show Output protocol connected (ne_filter)). On that machine the single large receive returns the right number of bytes with a damaged tail, and bounded reads make it work. I haven't been able to confirm the mechanism, since packet capture isn't an option on that machine.
So this is "make receiving robust when a content filter sits in the path" rather than a bug in NearDrop as such. I understand if that's not something you want to carry. If you'd rather, I can add this caveat to the description, or close the PR.
On your second question: yes, I used AI (Claude) to help with the investigation and with writing this up. I've read through the change myself and tested it on real hardware, both the failing Mac and the one where it works, as listed under Testing.
Uh oh!
There was an error while loading. Please reload this page.
Problem
Receiving any file larger than ~512 KB fails silently. The consent prompt appears, the destination file is created in
~/Downloads, and then nothing is written. The sender's progress stays at 0% and it closes the connection after ~35 s. No error notification, no crash. This reproduces every time on my setup — macOS 26.6.2 (Apple Silicon), receiving from a Nothing Phone (1).Cause
receiveFrameAsync(length:)asksNWConnectionfor the whole frame in one call:File payloads arrive as ~512 KB frames. For a 524,456-byte frame this returns the right number of bytes, but they are not the bytes that were sent, so
Securemessage_SecureMessage(serializedData:)throwsmalformedProtobufand the receive loop stops. Handshake frames (100–300 bytes) are unaffected, which is why the connection gets all the way to the transfer before dying.The damage is not deterministic. Across runs the same frame failed as
malformedProtobuf, and asrequiredFieldMissing("d2dMessage.message|sequenceNumber")after the body decrypted. The frame's own length arithmetic was always self-consistent (0a+ varint(524418) + header 28 + body 524384 +たす 34 =わ 524456), but the trailing 34 bytes were never the12 20+ 32-byte signature field, and their contents differed every run.Ruled out along the way: SwiftProtobuf 1.21.0 (a synthetic buffer of the same shape parses fine), the
BANDWIDTH_UPGRADE_RETRYframe, and the decrypt path.Fix
Reassemble the frame from bounded reads. Nothing else changes; small frames still complete in a single read. Chunks accumulate in an
NSMutableData, matching howpayloadBuffersalready collects payloads in this file. Bounded reads seem like the more robust shape here regardless of what makes the single large receive misbehave.The 16 KB chunk size is a conservative pick, not a measured one. Larger chunks may work equally well and would mean fewer round trips through the receive callback — happy to change it.
Testing
Receiving from a Nothing Phone (1), where every one of these produced a 0-byte file before the change:
The reassembled frames pass the HMAC check, so verification is unaffected.