lima-vm/lima · error
sudo field must not contain newline characters
Error message
sudo field must not contain newline characters
What it means
writeSudoers rejects a `sudo` value containing a newline character. Each sudoers file line is written as `userName sudo`; an embedded newline would inject arbitrary sudoers directives — a security hazard — so the agent refuses up front rather than writing a corrupt /etc/sudoers.d/90-cloud-init-users.
Source
Thrown at pkg/guestagent/fakecloudinit/fakecloudinit_darwin.go:322
}
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)
}
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 == "" {View on GitHub (pinned to dd909d0973)
Solutions
- Make the `sudo` field a single line, e.g. `ALL=(ALL) NOPASSWD: ALL`
- Replace YAML block scalars (`|`, `>`) with a plain quoted scalar for the sudo field
- If multiple rules are needed, place a full sudoers drop-in manually instead of the sudo field
- Trim trailing newlines from values generated by scripts
Example fix
# before sudo: | ALL=(ALL) NOPASSWD: ALL # after sudo: "ALL=(ALL) NOPASSWD: ALL"
Defensive patterns
Strategy: validation
Validate before calling
func validateSudo(sudo string) error {
if strings.Contains(sudo, "\n") || strings.Contains(sudo, "\r") {
return errors.New("sudo must not contain newline characters")
}
return nil
} Try / catch
if err := processUserData(ctx, data); err != nil {
if strings.Contains(err.Error(), "must not contain newline") {
log.Printf("invalid sudo value in user-data: %v — flatten to one line", err)
}
} Prevention
- Use plain quoted scalars, never `|`/`>` blocks, for sudo values
- Trim trailing newlines from generated configuration values
- Lint user-data for multi-line sudo before limactl start
When it happens
Trigger: The user-data `users[].sudo` string passed through processUserData → createUser → writeSudoers contains '\n'. Happens with multi-line YAML block scalars (`|`/`>`), comma-separated rules split across lines, or copy-pasted Linux cloud-init snippets.
Common situations: Copy-pasting multi-rule sudoers stanzas from Linux tutorials; YAML block scalar accidentally adding a trailing newline; tooling joining multiple sudo entries into one field.
Related errors
- invalid fstab entry: expected 6 fields, got %d: %v
- invalid pseudo tag for virtiofs: %#q
- failed to write sudoers file for user %#q: %w
- path is required for write_files entry
- invalid group %#q: %w
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/bda665e166408e18.
Report an issue: GitHub.