[GH-ISSUE #15320] Transfer-based pulls restart from scratch after interruption, unlike the standard downloader #35559

Open
opened 2026-04-22 20:08:34 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @addu2612 on GitHub (Apr 4, 2026).
Original GitHub issue: https://github.com/ollama/ollama/issues/15320

Hi, I dug through the repo after hitting an interrupted large-model download and found what looks like a real inconsistency in pull behavior.

The standard downloader in server/download.go already has resumable behavior built in. It keeps partial state, tracks per-part progress, and resumes with HTTP Range requests. That part looks pretty solid.

But the transfer path seems different.

server/images.go routes some pulls through pullWithTransfer(...), and x/imagegen/transfer/transfer.go explicitly says:

No resume: If a transfer fails, it restarts from scratch.

From x/imagegen/transfer/download.go, the current behavior also seems to match that comment:

  • each blob is downloaded as a whole
  • data is written to dest + ".tmp"
  • the temp file is removed on failure
  • retries restart the blob from zero
  • there is no Range-based continuation for partial .tmp files

So depending on which pull path gets selected, interrupted downloads can behave very differently:

  • standard downloader: resumable
  • transfer downloader: restart from scratch

That makes pull recovery feel inconsistent, especially for larger downloads or unstable connections.

Why this seems worth fixing

For multi-GB model pulls, restarting from zero after a transient interruption is painful in time, bandwidth, and trust. Even if the transfer path was originally optimized for many small blobs, the user-facing behavior still ends up feeling unreliable compared to the standard path.

Possible direction

A relatively small step toward parity might be:

  1. keep the .tmp file on transient failure
  2. detect its current size on retry
  3. resume with Range: bytes=<offset>-
  4. append to the temp file
  5. verify digest and size at the end
  6. delete temp only on corruption or invalid state

That would keep the transfer path simple while avoiding full restart-from-zero behavior.

If resumability is intentionally out of scope for transfer, even clearer progress/log messaging would help a lot so users know that retries on this path are restarting the current blob rather than resuming.

Relevant files

  • server/download.go
  • server/images.go
  • x/imagegen/transfer/transfer.go
  • x/imagegen/transfer/download.go

I also found an older issue around resumable downloads, but this one seems narrower and more current: the main downloader already has resume logic, while the transfer path explicitly does not.

Happy to take a stab at this if the direction makes sense.

Originally created by @addu2612 on GitHub (Apr 4, 2026). Original GitHub issue: https://github.com/ollama/ollama/issues/15320 Hi, I dug through the repo after hitting an interrupted large-model download and found what looks like a real inconsistency in pull behavior. The standard downloader in `server/download.go` already has resumable behavior built in. It keeps partial state, tracks per-part progress, and resumes with HTTP `Range` requests. That part looks pretty solid. But the transfer path seems different. `server/images.go` routes some pulls through `pullWithTransfer(...)`, and `x/imagegen/transfer/transfer.go` explicitly says: > No resume: If a transfer fails, it restarts from scratch. From `x/imagegen/transfer/download.go`, the current behavior also seems to match that comment: - each blob is downloaded as a whole - data is written to `dest + ".tmp"` - the temp file is removed on failure - retries restart the blob from zero - there is no `Range`-based continuation for partial `.tmp` files So depending on which pull path gets selected, interrupted downloads can behave very differently: - standard downloader: resumable - transfer downloader: restart from scratch That makes pull recovery feel inconsistent, especially for larger downloads or unstable connections. ## Why this seems worth fixing For multi-GB model pulls, restarting from zero after a transient interruption is painful in time, bandwidth, and trust. Even if the transfer path was originally optimized for many small blobs, the user-facing behavior still ends up feeling unreliable compared to the standard path. ## Possible direction A relatively small step toward parity might be: 1. keep the `.tmp` file on transient failure 2. detect its current size on retry 3. resume with `Range: bytes=<offset>-` 4. append to the temp file 5. verify digest and size at the end 6. delete temp only on corruption or invalid state That would keep the transfer path simple while avoiding full restart-from-zero behavior. If resumability is intentionally out of scope for transfer, even clearer progress/log messaging would help a lot so users know that retries on this path are restarting the current blob rather than resuming. ## Relevant files - `server/download.go` - `server/images.go` - `x/imagegen/transfer/transfer.go` - `x/imagegen/transfer/download.go` I also found an older issue around resumable downloads, but this one seems narrower and more current: the main downloader already has resume logic, while the transfer path explicitly does not. Happy to take a stab at this if the direction makes sense.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/ollama#35559