Skip to content

fix(downloader): hash the partial file before issuing the resume request - #11099

Merged
mudler merged 1 commit into
masterfrom
fix/downloader-resume-hash-stall
Jul 24, 2026
Merged

fix(downloader): hash the partial file before issuing the resume request#11099
mudler merged 1 commit into
masterfrom
fix/downloader-resume-hash-stall

Conversation

@localai-bot

Copy link
Copy Markdown
Collaborator

What this fixes

On a resumed download, the stall watchdog arms as soon as the HTTP response body exists, but the downloader then re-hashed the entire existing .partial before reading a single byte from the network. Nothing resets the watchdog while hashing, so if the hash outlasts DownloadStallTimeout (60s), the watchdog closes the body and the resume dies with:

download stalled: no data received for 1m0s

Since the partial never grows, every retry re-pays the same hash and fails identically: the install wedges permanently.

This is not theoretical: on a k8s deployment with /models on a CIFS/SMB share reading at ~117MB/s, a 7.9GB partial takes ~67s to hash, and every HF import retry died at exactly the 60s window while curl from the same pod streamed from the HF CDN at full speed. Any partial over ~7GB on such storage hits this; fast local NVMe hashes in seconds, which is why it never showed up in dev.

The fix

Open the partial and hash it before issuing the HTTP request. The watchdog now measures only actual network idle time, and the origin no longer sits on an idle connection while the hash runs.

Test

TDD: new spec in stall_test.go resumes from a 2GiB sparse partial (hashing reliably outlasts a 150ms stall window) against a server that answers the Range request promptly. It failed with the exact production error before the reorder and passes after. Full downloader suite (59 specs) green, also under -race.

🤖 Generated with Claude Code

The stall watchdog arms as soon as the response body exists, but the
downloader then re-hashed the entire existing .partial before reading a
single byte from the network. On slow models storage (a CIFS share
reading at ~117MB/s) hashing a multi-GB partial outlasts the 60s stall
window, so the watchdog aborted every healthy resume with 'download
stalled: no data received for 1m0s'. The partial never grew, so every
retry re-paid the same hash and failed identically, wedging the install
permanently (any partial over ~7GB on such storage).

Open the partial and hash it before the HTTP request instead: the
watchdog now only measures actual network idle time, and the origin no
longer sits on an idle connection while the hash runs.

Assisted-by: Claude:claude-fable-5 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
@mudler
mudler merged commit 90d93c7 into master Jul 24, 2026
62 of 67 checks passed
@mudler
mudler deleted the fix/downloader-resume-hash-stall branch July 24, 2026 10:57
@localai-bot localai-bot added the bug Something isn't working label Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants