jdx/mise · error

absent bootstrap user '{name}' may only set state and remove

Error message

absent bootstrap user '{name}' may only set state and remove_home

What it means

UserRequest::from_toml enforces that an absent user (state = "absent") may configure nothing except state and remove_home. If any of uid, group, groups, exclusive_groups, home, shell, comment, system, create_home, or move_home is set on an absent user, parsing bails. Deletion requests are name-only by design, so leftover creation attributes are treated as a config error rather than silently ignored.

Source

Thrown at src/system/accounts.rs:350

        if config.state == AccountState::Present && config.group.is_none() {
            bail!("present bootstrap user '{name}' requires a primary group");
        }
        if config.state == AccountState::Present && config.remove_home {
            bail!("present bootstrap user '{name}' must not set remove_home");
        }
        if config.state == AccountState::Absent
            && (config.uid.is_some()
                || config.group.is_some()
                || config.groups.is_some()
                || config.exclusive_groups
                || config.home.is_some()
                || config.shell.is_some()
                || config.comment.is_some()
                || config.system
                || config.create_home.is_some()
                || config.move_home)
        {
            bail!("absent bootstrap user '{name}' may only set state and remove_home");
        }
        if config.exclusive_groups && config.groups.is_none() {
            bail!("bootstrap user '{name}' sets exclusive_groups without groups");
        }
        if config.move_home && config.home.is_none() {
            bail!("bootstrap user '{name}' sets move_home without home");
        }
        if let Some(group) = &config.group {
            validate_name("group", group)?;
        }
        if let Some(path) = &config.home {
            validate_account_path(&name, "home", path)?;
        }
        if let Some(path) = &config.shell {
            validate_account_path(&name, "shell", path)?;
        }
        if config
            .comment

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Strip the absent user's table down to state = "absent" (plus remove_home = true only if the home directory should be deleted).
  2. If the attributes are needed, the user should be state = "present" instead.
  3. Re-run `mise bootstrap plan` to verify.

Example fix

# before
[bootstrap.users.oldsvc]
state = "absent"
uid = 900
shell = "/usr/sbin/nologin"
# after
[bootstrap.users.oldsvc]
state = "absent"
remove_home = true
Defensive patterns

Strategy: validation

Validate before calling

python3 - <<'EOF'
import sys, tomllib
ABSENT_OK = {'state', 'remove_home'}
cfg = tomllib.load(open('mise.toml','rb'))
for name, u in cfg.get('bootstrap', {}).get('users', {}).items():
    if u.get('state') == 'absent' and set(u) - ABSENT_OK:
        extra = sorted(set(u) - ABSENT_OK)
        sys.exit(f"absent user '{name}' sets disallowed keys: {extra}")
EOF

Type guard

ABSENT_ALLOWED = {'state', 'remove_home'}
def absent_user_is_name_only(u: dict) -> bool:
    return set(u) <= ABSENT_ALLOWED if u.get('state') == 'absent' else True

Prevention

When it happens

Trigger: A [bootstrap.users.<name>] table with state = "absent" that also sets e.g. uid = 1200, shell = "/bin/bash", or groups = [...], while `mise bootstrap` loads the config.

Common situations: Converting a fully-specified present user to absent by changing only the state field; deprovisioning configs generated from provisioning templates; merging config layers where an absent entry inherits extra keys.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/8e89f13841b3c347. Report an issue: GitHub.