lima-vm/lima · error

failed to remove stale raw image %q: %w

Error message

failed to remove stale raw image %q: %w

What it means

Before promoting the freshly converted raw image into place, ensureRawInCache removes any stale existing imgconv/raw file. If os.Remove fails for a reason other than the file not existing, the conversion is aborted with this wrapped error. This is a pre-rename cleanup step guarding the atomic replacement of the cached raw image.

Source

Thrown at pkg/downloader/downloader.go:497

	// Ensure the image is sparse to save cache space.
	rawTmpF, err := os.OpenFile(rawPathTmp, os.O_RDWR, 0o644)
	if err != nil {
		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 {

View on GitHub (pinned to dd909d0973)

Solutions

  1. Fix ownership/permissions on the cache imgconv directory: chown/chmod so the current user can delete files
  2. Ensure no running VM or qemu-img process still uses the cached raw image; stop instances before re-downloading
  3. Manually remove the stale file/directory (<cacheDir>/download/by-url-sha256/<hash>/imgconv) and retry
  4. Check for immutable flags (chattr -i) or read-only mounts on the cache volume

Example fix

// before: permission denied removing stale raw image
$ ls -l ~/.lima/_cache/download/by-url-sha256/<hash>/imgconv/raw  # owned by root
// after
$ sudo chown -R $(whoami) ~/.lima/_cache  # or delete: rm -rf .../imgconv
Defensive patterns

Strategy: validation

Validate before calling

rawImgConvPath := filepath.Join(cacheDir, "download", "by-url-sha256", key, "imgconv", "raw")
if _, err := os.Stat(rawImgConvPath); err == nil {
    if err := os.Remove(rawImgConvPath); err != nil {
        return fmt.Errorf("cannot clear stale raw image (check perms/locks): %w", err)
    }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to remove stale raw image") {
    // stop VMs, fix ownership, retry
}

Prevention

When it happens

Trigger: An old imgconv/raw exists in the cache and os.Remove returns e.g. EACCES/EPERM (read-only dir or file), EBUSY (file held by a running VM/qemu), or EISDIR/immutable attribute — anything besides ErrNotExist.

Common situations: Cache directory or files made read-only by permissions or ownership (e.g. created by root, now run as user); a previous VM process still holds the raw image open; immutable flag set after a crash-recovery tool ran.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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