lima-vm/lima · error

failed to open sudoers file %#q: %w

Error message

failed to open sudoers file %#q: %w

What it means

Opening /etc/sudoers.d/90-cloud-init-users with O_CREATE|O_WRONLY|O_APPEND and mode 0400 failed. The agent appends each cloud-init user's sudo rule to this shared drop-in file; failure means sudo grants cannot be installed for this user.

Source

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

			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)
	}
	sudoersPath := "/etc/sudoers.d/90-cloud-init-users"
	f, err := os.OpenFile(sudoersPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o400)
	if err != nil {
		return fmt.Errorf("failed to open sudoers file %#q: %w", sudoersPath, err)
	}
	if _, err = fmt.Fprintf(f, "%s %s\n", userName, sudo); err != nil {
		_ = f.Close()
		return fmt.Errorf("failed to write to sudoers file %#q for user %#q: %w", sudoersPath, userName, err)
	}
	return f.Close()
}

func writeFiles(ctx context.Context, entry cloudinittypes.WriteFile) error {
	if entry.Path == "" {
		return errors.New("path is required for write_files entry")
	}
	perm := os.FileMode(0o644)
	if entry.Permissions != "" {
		p, err := strconv.ParseUint(entry.Permissions, 8, 32)
		if err != nil {
			return fmt.Errorf("invalid permissions %#q for path %#q: %w", entry.Permissions, entry.Path, err)
		}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Run the guestagent as root (opening/creating under /etc requires it)
  2. Check ownership/permissions of /etc/sudoers.d/90-cloud-init-users and correct them
  3. Verify the volume containing /etc is writable and has free space
  4. As a last resort, delete the instance and reprovision cleanly

Example fix

// inside the VM (recovery)
sudo chown root:wheel /etc/sudoers.d/90-cloud-init-users
sudo chmod 400 /etc/sudoers.d/90-cloud-init-users
Defensive patterns

Strategy: try-catch

Validate before calling

if os.Geteuid() != 0 {
    return errors.New("opening /etc/sudoers.d/90-cloud-init-users requires root")
}
if _, err := os.OpenFile("/etc/sudoers.d/90-cloud-init-users", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o400); err != nil {
    return fmt.Errorf("sudoers drop-in not writable: %w", err)
} else { /* close handle in real code */ }

Try / catch

var pe *os.PathError
if errors.As(err, &pe) && strings.Contains(err.Error(), "open sudoers file") {
    log.Printf("sudoers open failed at %s: %v (perm=%d)", pe.Path, pe.Err, pe.Perm)
    // remediate: fix ownership/perms or rerun as root
}

Prevention

When it happens

Trigger: writeSudoers gets an error from os.OpenFile on the sudoers drop-in: the parent directory is unwritable or missing, the existing file is owned by another user with restrictive bits and the agent is not root, or an immutable/flags restriction applies.

Common situations: A previous run left 90-cloud-init-users owned by root with mode 0400 and the current process is not root; MDM/security tooling protecting /etc/sudoers.d; ENOSPC or EROFS on /etc.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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