{"record":{"id":"1ab3d0fea1b651f6","repo":"jdx/mise","slug":"bootstrap-user-requires-group-group-but","errorCode":null,"errorMessage":"bootstrap user '{}' requires group '{group}', but that group is absent","messagePattern":"bootstrap user '(.+?)' requires group '(.+?)', but that group is absent","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/accounts.rs","lineNumber":608,"sourceCode":"    }\n}\n\nfn validate_requests(groups: &[GroupRequest], users: &[UserRequest]) -> Result<()> {\n    let managed_groups = groups\n        .iter()\n        .map(|group| (group.name.as_str(), group.state))\n        .collect::<IndexMap<_, _>>();\n    for user in users\n        .iter()\n        .filter(|user| user.state == AccountState::Present)\n    {\n        for group in user\n            .group\n            .iter()\n            .chain(user.groups.iter().flat_map(|groups| groups.iter()))\n        {\n            match managed_groups.get(group.as_str()) {\n                Some(AccountState::Absent) => bail!(\n                    \"bootstrap user '{}' requires group '{group}', but that group is absent\",\n                    user.name\n                ),\n                Some(AccountState::Present) => {}\n                None if nix::unistd::Group::from_name(group)?.is_none() => bail!(\n                    \"bootstrap user '{}' requires undeclared group '{group}'\",\n                    user.name\n                ),\n                None => {}\n            }\n        }\n    }\n    Ok(())\n}\n\nfn inspect_user(name: &str, desired_uid: Option<u32>) -> Result<UserInspection> {\n    let Some(user) = nix::unistd::User::from_name(name)? else {\n        return match desired_uid {","sourceCodeStart":590,"sourceCodeEnd":626,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/accounts.rs#L590-L626","documentation":"validate_requests cross-checks group references: for every present user, each referenced group (primary via group= and supplementary via groups=) is looked up in the managed groups map; if config explicitly declares that group state = \"absent\", the configuration is self-contradictory (create/keep a user that needs a group you are deleting) and parsing fails with both names.","triggerScenarios":"A mise.toml where [bootstrap.groups.deploy] has state = \"absent\" while some [bootstrap.users.<name>] with state = \"present\" lists deploy in group = \"deploy\" or groups = [\"deploy\"].","commonSituations":"Deprovisioning a shared group but forgetting the users that depend on it; config layers disagreeing (one layer removes the group, another adds a user to it); renaming a group in one place only.","solutions":["Decide the group's fate: if users need it, set the group state = \"present\" (or delete its absent entry).","If the group should go, first remove/change the users that reference it (state = \"absent\" or new group/groups values).","Watch for config layering: check `mise config ls`/layered output for a layer declaring the group absent."],"exampleFix":"# before\n[bootstrap.groups.deploy]\nstate = \"absent\"\n[bootstrap.users.app]\nstate = \"present\"\ngroup = \"deploy\"\n# after — either keep the group\n[bootstrap.groups.deploy]\nstate = \"present\"\n# or move the user off it\n[bootstrap.users.app]\nstate = \"present\"\ngroup = \"app\"","handlingStrategy":"validation","validationCode":"python3 - <<'EOF'\nimport sys, tomllib\ncfg = tomllib.load(open('mise.toml','rb'))\nb = cfg.get('bootstrap', {})\nmanaged = {n: g.get('state') for n, g in b.get('groups', {}).items()}\nfor name, u in b.get('users', {}).items():\n    if u.get('state') != 'present':\n        continue\n    refs = ([u['group']] if 'group' in u else []) + list(u.get('groups', []))\n    for g in refs:\n        if managed.get(g) == 'absent':\n            sys.exit(f\"user '{name}' needs group '{g}' but config marks it absent\")\nEOF","typeGuard":"def user_groups_not_absent(user: dict, managed_group_states: dict) -> bool:\n    refs = ([user['group']] if 'group' in user else []) + list(user.get('groups', []))\n    return all(managed_group_states.get(g) != 'absent' for g in refs)","tryCatchPattern":null,"preventionTips":["When deprovisioning a group, grep the config for users referencing it first.","Keep group lifecycle and its users in the same config file/review.","Check layered configs (`mise config ls`) for a layer that removes a group another layer uses."],"tags":["mise","bootstrap","users","groups","config-validation","dependency-conflict"],"backgroundTag":"config-validation-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}