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
- Run the guestagent as root (opening/creating under /etc requires it)
- Check ownership/permissions of /etc/sudoers.d/90-cloud-init-users and correct them
- Verify the volume containing /etc is writable and has free space
- 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
- Run provisioning as root
- Check ownership of /etc/sudoers.d/90-cloud-init-users after previous runs
- Avoid security tools that lock /etc/sudoers.d during guest boot
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
- failed to create /etc/sudoers.d directory: %w
- failed to write to sudoers file %#q for user %#q: %w
- failed to create parent directory for path %#q: %w
- failed to create temp file: %w
- failed to write temp plist: %w
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/40c62c8edc420760.
Report an issue: GitHub.