{"record":{"id":"96e6bae7d077106a","repo":"lima-vm/lima","slug":"failed-to-write-sudoers-file-for-user-q-w","errorCode":null,"errorMessage":"failed to write sudoers file for user %#q: %w","messagePattern":"failed to write sudoers file for user %#q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/guestagent/fakecloudinit/fakecloudinit_darwin.go","lineNumber":312,"sourceCode":"\tlogrus.Infof(\"Created user %#q. The password is stored in %#q\", u.Name, pwPath)\n\n\tdotSSHPath := filepath.Join(homedir, \".ssh\")\n\tif err = os.MkdirAll(dotSSHPath, 0o700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create .ssh directory for user %#q: %w\", u.Name, err)\n\t}\n\tauthKeysPath := filepath.Join(dotSSHPath, \"authorized_keys\")\n\tauthKeysContent := strings.Join(u.SSHAuthorizedKeys, \"\\n\")\n\tif err = os.WriteFile(authKeysPath, []byte(authKeysContent), 0o600); err != nil {\n\t\treturn fmt.Errorf(\"failed to write authorized_keys file for user %#q: %w\", u.Name, err)\n\t}\n\tfor _, f := range []string{pwPath, dotSSHPath, authKeysPath} {\n\t\tif err = os.Chown(f, uid, -1); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to chown %#q for user %#q: %w\", f, u.Name, err)\n\t\t}\n\t}\n\tif u.Sudo != \"\" {\n\t\tif err := writeSudoers(u.Name, u.Sudo); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write sudoers file for user %#q: %w\", u.Name, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// writeSudoers appends a sudoers entry for the given user.\n// writeSudoers is expected be called only once on creating the user account.\nfunc writeSudoers(userName, sudo string) error {\n\tif strings.Contains(sudo, \"\\n\") {\n\t\treturn errors.New(\"sudo field must not contain newline characters\")\n\t}\n\tif err := os.MkdirAll(\"/etc/sudoers.d\", 0o700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create /etc/sudoers.d directory: %w\", err)\n\t}\n\tsudoersPath := \"/etc/sudoers.d/90-cloud-init-users\"\n\tf, err := os.OpenFile(sudoersPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o400)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open sudoers file %#q: %w\", sudoersPath, err)","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/lima-vm/lima/blob/dd909d0973cd84fa35f9e1693181b4585ea616c1/pkg/guestagent/fakecloudinit/fakecloudinit_darwin.go#L294-L330","documentation":"Wraps any failure from writeSudoers while installing the sudoers entry for a user who has a non-empty `sudo` field in user-data. It is a pass-through wrapper: the meaningful cause is inside (newline validation, /etc/sudoers.d creation, open, or write failure). Provisioning of the user is aborted.","triggerScenarios":"createUser calls writeSudoers(u.Name, u.Sudo) when u.Sudo != \"\" and writeSudoers returns any error — including 'sudo field must not contain newline characters', '/etc/sudoers.d' mkdir failure, open failure, or write failure.","commonSituations":"User-data `sudo` value copied from Linux cloud-init examples spanning multiple lines (e.g. nested sudoers rules); read-only root volume; malformed multi-line YAML producing embedded newlines.","solutions":["Read the wrapped inner error to identify the real cause","Ensure the user-data `sudo` field is a single line (e.g. `ALL=(ALL) NOPASSWD: ALL`)","Verify the root filesystem is writable and /etc/sudoers.d can be created","Remove the `sudo` field if sudo access is not needed, avoiding this code path entirely"],"exampleFix":"# before (multi-line sudo value)\nusers:\n- name: alice\n  sudo: \"ALL=(ALL) NOPASSWD: ALL,\n    ALL=(ALL) NOPASSWD: /usr/bin/ls\"\n# after\nusers:\n- name: alice\n  sudo: \"ALL=(ALL) NOPASSWD: ALL\"","handlingStrategy":"validation","validationCode":"// validate user-data sudo fields before applying\nfor _, u := range users {\n    if u.Sudo != \"\" && strings.ContainsAny(u.Sudo, \"\\n\\r\") {\n        return fmt.Errorf(\"user %s: sudo must be a single line\", u.Name)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := processUserData(ctx, data); err != nil {\n    if strings.Contains(err.Error(), \"sudoers file\") {\n        log.Printf(\"sudoers provisioning failed: %v — check the wrapped cause\", err)\n    }\n}","preventionTips":["Keep `sudo` values single-line (e.g. ALL=(ALL) NOPASSWD: ALL)","Avoid YAML block scalars for the sudo field","Omit the sudo field entirely when sudo access is not needed"],"tags":["sudoers","macos","guestagent","cloud-init"],"backgroundTag":"sudoers-write-failed","analyzedSha":"dd909d0973cd84fa35f9e1693181b4585ea616c1","analyzedAt":"2026-09-01T14:24:59.842Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}