lima-vm/lima · error

failed to replace original with raw image

Error message

failed to replace original with raw image: %w

What it means

After removing any stale entry, ensureRawInCache atomically renames imgconv/raw.tmp to imgconv/raw. If os.Rename fails (raw.tmp vanished, cross-device rename, permissions), the converted image cannot replace the cached original and this error is returned. The defer os.Remove(rawPathTmp) cleans up the tmp file afterwards.

Solutions

  1. Avoid running multiple limactl downloads of the same image concurrently against the same cache; retry sequentially
  2. Verify the cache directory is on a single local filesystem so rename is atomic
  3. Check directory write permissions on the imgconv folder and fix with chown/chmod
  4. Clear the affected cache entry (<hash> dir) and re-download to get a consistent state
Defensive patterns

Strategy: retry

Try / catch

err := downloadOnce()
if err != nil && strings.Contains(err.Error(), "failed to replace original with raw image") {
    time.Sleep(2 * time.Second)
    err = downloadOnce() // racing tmp cleanup is transient; second attempt usually succeeds
}

Prevention

When it happens

Trigger: os.Rename(rawPathTmp, rawImgConvPath) returns ENOENT (tmp deleted concurrently by another downloader process), EXDEV (cache spans filesystems), or EACCES on the directory.

Common situations: Two limactl processes downloading the same image race and one's deferred cleanup deletes the other's raw.tmp; cache directory placed on a filesystem where rename across the imgconv dir fails; antivirus/indexers removing tmp files.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/2afb20b7ab996e05. Report an issue: GitHub.

Appendix: source

Thrown at pkg/downloader/downloader.go:500

		return "", "", fmt.Errorf("failed to open raw tmp file %q: %w", rawPathTmp, err)
	}
	fi, err := rawTmpF.Stat()
	if err != nil {
		_ = rawTmpF.Close()
		return "", "", fmt.Errorf("failed to stat raw tmp file %q: %w", rawPathTmp, err)
	}
	if err := diskUtil.MakeSparse(ctx, rawTmpF, fi.Size()); err != nil {
		logrus.WithError(err).Warnf("Failed to make %q sparse (non-fatal)", rawPathTmp)
	}
	if err := rawTmpF.Close(); err != nil {
		return "", "", fmt.Errorf("failed to close raw tmp file %q: %w", rawPathTmp, err)
	}

	if err := os.Remove(rawImgConvPath); err != nil && !errors.Is(err, os.ErrNotExist) {
		return "", "", fmt.Errorf("failed to remove stale raw image %q: %w", rawImgConvPath, err)
	}
	if err := os.Rename(rawPathTmp, rawImgConvPath); err != nil {
		return "", "", fmt.Errorf("failed to replace original with raw image: %w", err)
	}

	algo := digest.Canonical
	if originalDigest != "" {
		algo = originalDigest.Algorithm()
	}
	rawDigest, err := calculateFileDigest(rawImgConvPath, algo)
	if err != nil {
		return "", "", fmt.Errorf("failed to calculate digest of raw image: %w", err)
	}

	rawDigestPath := filepath.Join(imgConvPath, "raw.digest")
	rawDigestPathTmp := rawDigestPath + ".tmp"
	defer os.Remove(rawDigestPathTmp)
	if err := os.WriteFile(rawDigestPathTmp, []byte(rawDigest.String()), 0o644); err != nil {
		return "", "", fmt.Errorf("failed to write raw digest file: %w", err)
	}
	if err := os.Remove(rawDigestPath); err != nil && !errors.Is(err, os.ErrNotExist) {

View on GitHub (pinned to dd909d0973)