lima-vm/lima · error

failed to write authorized_keys file for user %#q

Error message

failed to write authorized_keys file for user %#q: %w

What it means

Writing the `authorized_keys` file (`<homedir>/.ssh/authorized_keys`, mode 0600) failed after the `.ssh` directory was created successfully. Without this file SSH public-key login for the user cannot work, so provisioning aborts. The wrapped os.WriteFile error carries the actual cause.

Solutions

  1. Check guest disk space and free space if needed
  2. Verify `<homedir>/.ssh` exists as a writable directory (mode 0700)
  3. Inspect the wrapped errno (EACCES/ENOSPC/EIO) in the log
  4. Re-run provisioning after fixing; keys come from the user-data `users[].ssh_authorized_keys` field

Example fix

// before: instance disk full
limactl disk resize / grow instance disk
// after: reprovision
limactl start instance.yaml
Defensive patterns

Strategy: validation

Validate before calling

dotSSH := filepath.Join(home, ".ssh")
st, err := os.Stat(dotSSH)
if err != nil || !st.IsDir() || st.Mode().Perm()&0o200 == 0 {
    return fmt.Errorf("%s must exist as a writable directory", dotSSH)
}

Try / catch

if err := processUserData(ctx, data); err != nil {
    var pe *os.PathError
    if errors.As(err, &pe) && strings.Contains(err.Error(), "authorized_keys") {
        log.Printf("authorized_keys write failed at %s: %v", pe.Path, pe.Err)
    }
}

Prevention

When it happens

Trigger: os.WriteFile(authKeysPath, joined SSH keys, 0o600) fails in createUser during processUserData. Causes: ENOSPC, EACCES on the just-created directory, I/O errors, or security software blocking writes into .ssh.

Common situations: Disk-full VM during heavy provisioning; EDR/antivirus on macOS guest restricting .ssh writes; extremely long ssh key lists hitting quota/limits.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

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

	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)
	}

	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") {

View on GitHub (pinned to dd909d0973)