On the macOS CI runner, a re-fetch whose connection dies before the status line arrives overwrites the mirrored file with zero bytes. The same crawl on Linux leaves the previous copy alone.
Seen in run 30220185765 (job "build (macOS arm64, clang)"), on a fixture that serves /changes/reset.bin in full on the first fetch and, on every fetch after that, closes the connection without answering. The change report for pass 2 puts the file under changed with:
{ "url": ".../changes/reset.bin", "file": "127.0.0.1_50718/changes/reset.bin", "size": 0, "previous_size": 2062 }
size is a plain fsize_utf8() of the path after the crawl, so the 2062 bytes pass 1 mirrored are gone and a zero-length file is in their place. Three attempts (--retries=2) all failed, so nothing was ever received to write.
The suspect is the save gate in htscore.c (around the "An empty body must not overwrite the file when the transfer failed" comment): it lets a zero-length body through on r.size == 0 && r.statuscode > 0. If the Darwin socket path leaves a positive status code behind on a connection that produced no response, filesave() runs and truncates. I have not reproduced it locally, so which of the two differs, the status code or the body pointer, is still open.
Related to #746 and to the #562 family: a failed re-fetch must not damage the copy it failed to refresh. This one destroys the bytes directly rather than through the purge.
On the macOS CI runner, a re-fetch whose connection dies before the status line arrives overwrites the mirrored file with zero bytes. The same crawl on Linux leaves the previous copy alone.
Seen in run 30220185765 (job "build (macOS arm64, clang)"), on a fixture that serves
/changes/reset.binin full on the first fetch and, on every fetch after that, closes the connection without answering. The change report for pass 2 puts the file underchangedwith:sizeis a plainfsize_utf8()of the path after the crawl, so the 2062 bytes pass 1 mirrored are gone and a zero-length file is in their place. Three attempts (--retries=2) all failed, so nothing was ever received to write.The suspect is the save gate in
htscore.c(around the "An empty body must not overwrite the file when the transfer failed" comment): it lets a zero-length body through onr.size == 0 && r.statuscode > 0. If the Darwin socket path leaves a positive status code behind on a connection that produced no response,filesave()runs and truncates. I have not reproduced it locally, so which of the two differs, the status code or the body pointer, is still open.Related to #746 and to the #562 family: a failed re-fetch must not damage the copy it failed to refresh. This one destroys the bytes directly rather than through the purge.