lima-vm/lima · error

unexpected symlink target for virtiofs mount source %#q: exp

Error message

unexpected symlink target for virtiofs mount source %#q: expected %#q, got %#q

What it means

Raised by mountVirtiofs when the destination directory already exists as a symlink but points to a different target than the expected /Volumes/My Shared Files/<pseudoTag> location. The agent refuses to silently repoint an existing symlink it did not create, reporting expected vs got targets.

Source

Thrown at pkg/guestagent/fakecloudinit/fakecloudinit_darwin.go:176

// mountVirtiofs symlinks `/Volumes/My Shared Files/<pseudoTag>` (automatically mounted by macOS) to dir.
// dir must not exist, or, must be a symlink to the expected source.
func mountVirtiofs(pseudoTag, dir string) error {
	if strings.Contains(pseudoTag, string(filepath.Separator)) {
		return fmt.Errorf("invalid pseudo tag for virtiofs: %#q", pseudoTag)
	}
	lnSrc := filepath.Join("/Volumes/My Shared Files", pseudoTag)

	// FIXME: verify that the filesystem of lnSrc is indeed read-only when user-data contains the "ro" option.
	// unix.Statfs() could be used, but unix.Statfs_t.Flags & unix.MNT_RDONLY seems always 0 for virtiofs.
	// `mount -v` does not show "ro" flag either.

	lnExisting, err := os.Readlink(dir)
	if err == nil {
		if lnExisting == lnSrc {
			return nil // already symlinked
		}
		return fmt.Errorf("unexpected symlink target for virtiofs mount source %#q: expected %#q, got %#q", lnSrc, lnSrc, lnExisting)
	}
	if !errors.Is(err, os.ErrNotExist) {
		logrus.WithError(err).Warnf("Failed to read existing symlink for virtiofs mount source %#q", lnSrc)
	}
	if err := os.Symlink(lnSrc, dir); err != nil {
		return fmt.Errorf("failed to create symlink from %#q to %#q: %w", lnSrc, dir, err)
	}
	return nil
}

func setTimezone(ctx context.Context, timezone string) error {
	cmd := exec.CommandContext(ctx, "systemsetup", "-settimezone", timezone)
	logrus.Infof("Executing command: %v", cmd.Args)
	if output, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("failed to execute command %v: %w (output=%#q)", cmd.Args, err, output)
	}
	return nil
}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Read the `got` target in the message to identify what currently occupies the path.
  2. Delete or update the stale symlink in the guest (e.g. `sudo rm /mnt/share` in the VM) so the next boot can recreate it correctly.
  3. Make sure the same destination path is not used by multiple mounts entries with different sources.
  4. If the existing target is actually correct, align the user-data pseudo tag with it instead of the other way around.

Example fix

// inside guest VM
// before
$ ls -l /mnt/share  # -> /Volumes/My Shared Files/OldTag
$ sudo rm /mnt/share
// after (let fakecloudinit recreate it)
$ ls -l /mnt/share  # -> /Volumes/My Shared Files/ExpectedTag
Defensive patterns

Strategy: validation

Validate before calling

func checkSymlinkTarget(dir, expected string) error {
  if existing, err := os.Readlink(dir); err == nil && existing != expected {
    return fmt.Errorf("%s is a symlink to %s, expected %s - remove it before provisioning", dir, existing, expected)
  }
  return nil
}

Type guard

func isSymlinkTo(dir, expected string) bool {
  existing, err := os.Readlink(dir)
  return err == nil && existing == expected
}

Try / catch

lnExisting, err := os.Readlink(dir)
if err == nil && lnExisting != lnSrc {
  // decide: repair or fail
  if err := os.Remove(dir); err != nil { return fmt.Errorf("stale symlink %s: %w", dir, err) }
}

Prevention

When it happens

Trigger: os.Readlink(dir) succeeds but the existing link target differs from lnSrc - e.g. a previous boot used a different pseudo tag, the user manually created the symlink, or the destination was reused from an older instance configuration.

Common situations: Changing the Lima mount tag for the same guest destination path, pointing two different mounts at the same destination, or leftover symlinks from an earlier VM created on the same disk image.

Related errors


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