Fix file corruption when server ignores Range header on download retry - #3778
Conversation
When a download is interrupted (ReadTimeout/ConnectionError) and retried, http_get sends a Range header to resume. However, some servers (e.g. CloudFront with Accept-Encoding: gzip) ignore the Range header and return 200 with the full file instead of 206 Partial Content. Since the code didn't check the response status, it appended the full content to existing partial data, causing file corruption. The fix checks for this case: if resume_size > 0 but the server returned 200 (not 206), we truncate the file before writing since we're receiving the complete content.
Full Investigation ReportIncident: AMD GPU CI Build 4130
Timeline (2026-02-04 UTC)
CloudFront Evidence (Athena)All requests served from edge
Key observations:
Mathematical ProofThe file is served gzip-compressed by CloudFront (~487KB on the wire). Root CauseCloudFront ignores Python's Confirmed with curl: # With Accept-Encoding: gzip → Range is IGNORED → 200
curl -s -o /dev/null -w '%{http_code}' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Range: bytes=500-999' \
'https://huggingface.co/api/resolve-cache/models/TheBloke/TinyLlama-1.1B-Chat-v0.3-GPTQ/05835c52707fff57cecd16a364de2bc65c9bf102/tokenizer.json'
# → 200
# Without Accept-Encoding → Range is HONORED → 206
curl -s -o /dev/null -w '%{http_code}' \
-H 'Range: bytes=500-999' \
'https://huggingface.co/api/resolve-cache/models/TheBloke/TinyLlama-1.1B-Chat-v0.3-GPTQ/05835c52707fff57cecd16a364de2bc65c9bf102/tokenizer.json'
# → 206The BugIn The Fix4 lines: after the GET request, if |
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3778 +/- ##
==========================================
+ Coverage 75.00% 76.62% +1.61%
==========================================
Files 145 153 +8
Lines 13978 15090 +1112
==========================================
+ Hits 10484 11562 +1078
- Misses 3494 3528 +34 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
#3778) * Fix file corruption when server ignores Range header on download retry When a download is interrupted (ReadTimeout/ConnectionError) and retried, http_get sends a Range header to resume. However, some servers (e.g. CloudFront with Accept-Encoding: gzip) ignore the Range header and return 200 with the full file instead of 206 Partial Content. Since the code didn't check the response status, it appended the full content to existing partial data, causing file corruption. The fix checks for this case: if resume_size > 0 but the server returned 200 (not 206), we truncate the file before writing since we're receiving the complete content. * Fix ruff formatting: remove extra blank line
Summary
http_get()when a download retry'sRangeheader is ignored by the serverRangeheader. CloudFront (and other CDNs) ignoreRangewhenAccept-Encoding: gzipis present, returning200 OKwith the full file instead of206 Partial Content. The code didn't check the response status, so it appended the full file to the existing partial data, corrupting the fileresume_size > 0butstatus_code == 200) and truncates the file before writingContext
Discovered via a CI failure where
tokenizer.json(expected 1,843,320 bytes) ended up as 3,920,264 bytes after two retries:The root cause is that Python's
httpx(andrequests) automatically sendsAccept-Encoding: gzip, deflate. CloudFront cannot serve a Range response when content-encoding is applied, so it ignores theRangeheader and returns200with the full (compressed) content. The client transparently decompresses it and appends it to the partial file.Confirmed with curl:
Note
Low Risk
Small, localized change to retry/download write behavior plus a targeted unit test; low risk aside from potential edge cases around resume/truncate semantics for unusual servers.
Overview
Fixes a corruption case in
http_getretries: if a resumed download sends aRangeheader but the server responds with200 OK(ignoring the range), the partially written temp file is now truncated and the download restarts from byte 0 instead of appending full content onto partial data.Adds a regression test covering this scenario by simulating an interrupted download followed by a retry where the server returns
200with the full body, asserting the final file length/content are correct (not oversized from appends).Written by Cursor Bugbot for commit 9bc947e. This will update automatically on new commits. Configure here.