lima-vm/lima · error

failed to chown %#q for user %#q: %w

Error message

failed to chown %#q for user %#q: %w

What it means

os.Chown failed while assigning the user's UID to the password file, the `.ssh` directory, or `authorized_keys` (group is left unchanged via gid -1). The agent needs these files owned by the new user so SSH login and password retrieval work. The error names the exact file path (`%#q`) that failed.

Source

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

	pwPath := filepath.Join(homedir, "password")
	if err = os.WriteFile(pwPath, []byte(pw+"\n"), 0o400); err != nil {
		return fmt.Errorf("failed to write password file for user %#q: %w", u.Name, err)
	}
	logrus.Infof("Created user %#q. The password is stored in %#q", u.Name, pwPath)

	dotSSHPath := filepath.Join(homedir, ".ssh")
	if err = os.MkdirAll(dotSSHPath, 0o700); err != nil {
		return fmt.Errorf("failed to create .ssh directory for user %#q: %w", u.Name, err)
	}
	authKeysPath := filepath.Join(dotSSHPath, "authorized_keys")
	authKeysContent := strings.Join(u.SSHAuthorizedKeys, "\n")
	if err = os.WriteFile(authKeysPath, []byte(authKeysContent), 0o600); err != nil {
		return fmt.Errorf("failed to write authorized_keys file for user %#q: %w", u.Name, err)
	}
	for _, f := range []string{pwPath, dotSSHPath, authKeysPath} {
		if err = os.Chown(f, uid, -1); err != nil {
			return fmt.Errorf("failed to chown %#q for user %#q: %w", f, u.Name, err)
		}
	}
	if u.Sudo != "" {
		if err := writeSudoers(u.Name, u.Sudo); err != nil {
			return fmt.Errorf("failed to write sudoers file for user %#q: %w", u.Name, err)
		}
	}
	return nil
}

// writeSudoers appends a sudoers entry for the given user.
// writeSudoers is expected be called only once on creating the user account.
func writeSudoers(userName, sudo string) error {
	if strings.Contains(sudo, "\n") {
		return errors.New("sudo field must not contain newline characters")
	}
	if err := os.MkdirAll("/etc/sudoers.d", 0o700); err != nil {
		return fmt.Errorf("failed to create /etc/sudoers.d directory: %w", err)

View on GitHub (pinned to dd909d0973)

Solutions

  1. Confirm the guestagent runs as root (chown requires privileges)
  2. Check the log line 'Requested UID ... but the system assigned UID ...' for UID conflicts and pick a free UID in user-data
  3. Verify the UID in the users entry is numeric and not already in use (`dscl . -list /users UniqueID`)
  4. Inspect the wrapped errno (EPERM/EINVAL) for the failing path shown in the message

Example fix

// before
users:
- name: alice
  uid: "501"   # already used by the default macOS user
// after
users:
- name: alice
  uid: "502"
Defensive patterns

Strategy: validation

Validate before calling

// verify the requested UID is free on the macOS guest
out, err := exec.Command("dscl", ".", "-list", "/users", "UniqueID").Output()
if err == nil && strings.Contains(string(out), "\t"+uid+"\n") {
    return fmt.Errorf("uid %s already in use; pick another", uid)
}
if os.Geteuid() != 0 {
    return errors.New("chown requires root; run guestagent as root")
}

Try / catch

if err := processUserData(ctx, data); err != nil {
    var se *os.LinkError
    if errors.As(err, &se) && strings.Contains(err.Error(), "chown") {
        log.Printf("chown failed (perm=%d): %v — check UID conflicts and root privileges", se.Perm, se.Err)
    }
}

Prevention

When it happens

Trigger: os.Chown(f, uid, -1) returns an error in createUser for one of pwPath, dotSSHPath, or authKeysPath, during processUserData on darwin. Common causes: the requested UID does not exist/reserve on the system (e.g. macOS Setup Assistant assigned a different UID and the Lookup fallback was skipped or failed), or the process lacks root privileges.

Common situations: User-data requests a UID already taken or reserved on macOS; agent runs without root; chown restrictions from sandbox/MDM profiles; UID collision when a prior user with the same UID exists.

Related errors


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