jdx/mise · error

bootstrap user '{name}' sets exclusive_groups without groups

Error message

bootstrap user '{name}' sets exclusive_groups without groups

What it means

UserRequest::from_toml rejects a user that sets exclusive_groups = true without also setting groups. exclusive_groups means 'usergroup membership is exactly this list' (replaces supplementary groups), so without a groups list there is nothing to be exclusive over and the semantics are undefined; parsing fails with the user's name.

Source

Thrown at src/system/accounts.rs:353

        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
            .as_ref()
            .is_some_and(|comment| comment.contains([':', '\n', '\r']))
        {

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Add an explicit groups list that the user should be exclusive to, e.g. groups = ["deploy"].
  2. If you want the user removed from supplementary groups, list the exact remaining groups (possibly just the primary one's companions) — mise has no 'empty list' shorthand beyond an explicit empty array, so confirm behavior with `mise bootstrap plan`.
  3. If exclusivity was unintended, delete exclusive_groups.

Example fix

# before
[bootstrap.users.ci]
state = "present"
group = "ci"
exclusive_groups = true
# after
[bootstrap.users.ci]
state = "present"
group = "ci"
exclusive_groups = true
groups = ["docker"]
Defensive patterns

Strategy: validation

Validate before calling

python3 - <<'EOF'
import sys, tomllib
cfg = tomllib.load(open('mise.toml','rb'))
for name, u in cfg.get('bootstrap', {}).get('users', {}).items():
    if u.get('exclusive_groups') and 'groups' not in u:
        sys.exit(f"user '{name}' sets exclusive_groups without groups")
EOF

Type guard

def exclusive_groups_valid(u: dict) -> bool:
    return ('groups' in u) if u.get('exclusive_groups') else True

Prevention

When it happens

Trigger: A [bootstrap.users.<name>] table with exclusive_groups = true but no groups = [...] array, loaded during bootstrap config parsing.

Common situations: Intending 'remove the user from all extra groups' and assuming exclusive_groups alone does that; splitting a config and dropping the groups line; renaming the groups key (e.g. to extra_groups) so it no longer parses.

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/5246644b0b3a251d. Report an issue: GitHub.