abiosoft/colima · error

cannot truncate file for fresh download: %w

Error message

cannot truncate file for fresh download: %w

What it means

The server answered 200 (full body) while a partial file existed for resume (If-Range/ETag path failed or Range unsupported), so the code must restart from byte 0 and calls file.Truncate(0) on the .downloading partial — and the truncate failed. Reached only when existingSize > 0; the wrapped error is a filesystem failure on the partial (I/O error, storage dropped, file replaced concurrently).

Source

Thrown at util/downloader/http.go:160

	// execute request
	resp, err := h.client.Do(req)
	if err != nil {
		return nil, &NetworkError{Op: "download", URL: opts.URL, Err: err}
	}
	defer func() { _ = resp.Body.Close() }()

	// store final URL after redirects
	result.FinalURL = resp.Request.URL.String()
	result.ETag = resp.Header.Get("ETag")

	// handle response status
	switch resp.StatusCode {
	case http.StatusOK: // 200 - Full content (resume not supported or If-Range failed)
		if existingSize > 0 {
			// server sent full content, need to truncate and start over
			if err := file.Truncate(0); err != nil {
				return nil, fmt.Errorf("cannot truncate file for fresh download: %w", err)
			}
			if _, err := file.Seek(0, 0); err != nil {
				return nil, fmt.Errorf("cannot seek to start of file: %w", err)
			}
			existingSize = 0
		}
		result.TotalBytes = resp.ContentLength

	case http.StatusPartialContent: // 206 - Resume successful
		result.WasResumed = true
		// Content-Range: bytes 21010-47021/47022
		contentRange := resp.Header.Get("Content-Range")
		if totalSize := parseContentRangeTotal(contentRange); totalSize > 0 {
			result.TotalBytes = totalSize
		} else {
			result.TotalBytes = existingSize + resp.ContentLength
		}

View on GitHub (pinned to c3a5f9184d)

Solutions

  1. Delete the .downloading partial and the .resume info, then retry for a clean fresh download
  2. Move the cache dir onto reliable local storage
  3. Avoid concurrent downloads sharing the same cache dir

Example fix

# before: resume hits a 200, truncate fails on the poisoned partial
# after: force a fresh download
cache=$(limactl ... ) # sha256-named entry under <cachedir>/caches
rm -f "${cache}.downloading" "${cache}.resume"
Defensive patterns

Strategy: retry

Try / catch

err := fileDownloader.Download(req, dest)
if err != nil && strings.Contains(err.Error(), "cannot truncate file for fresh download") {
    base := strings.TrimSuffix(dest, ".downloading")
    _ = os.Remove(dest)                  // poisoned partial
    _ = os.Remove(base + ".resume")       // stale resume info
    err = fileDownloader.Download(req, dest)
}
if err != nil {
    return err
}

Prevention

When it happens

Trigger: Resume attempt against a server that ignores Range headers; the partial file sits on a network/external volume that errors on truncate; another process deleted/replaced the partial between open and truncate.

Common situations: Cache stored on flaky external storage; two colima processes resuming the same URL; disk faults.

Related errors


AI-assisted analysis of abiosoft/colima@c3a5f9184d (2026-08-15). Data as JSON: /api/errors/051392ec22b0342d. Report an issue: GitHub.