juanfont/headscale · error · ErrGroupMembersCannotBeRecursive
groups[%q]: %q: %w
Error message
groups[%q]: %q: %w
What it means
Groups may not reference other groups (ErrGroupMembersCannotBeRecursive). After unmarshalling, each member is checked with isGroup and any nested group reference is rejected, naming both the containing group and the member.
Source
Thrown at hscontrol/policy/v2/types.go:1372
return fmt.Errorf("%w: group %q got %T", ErrGroupValueNotArray, key, v)
}
}
// Reject group-in-group references. Reverse-sort the keys so the
// reported (parent, child) pair names the deepest non-leaf parent
// first.
keys := make([]string, 0, len(rawGroups))
for k := range rawGroups {
keys = append(keys, k)
}
slices.Sort(keys)
slices.Reverse(keys)
for _, key := range keys {
for _, u := range rawGroups[key] {
if isGroup(u) {
return fmt.Errorf("groups[%q]: %q: %w", key, u, ErrGroupMembersCannotBeRecursive)
}
}
}
*g = make(Groups)
for key, value := range rawGroups {
group := Group(key)
// Group name already validated above
var usernames Usernames
for _, u := range value {
username := Username(u)
err := username.Validate()
if err != nil {
return err
}View on GitHub (pinned to 565fd254d0)
Solutions
- Flatten the nested group: list the individual user emails directly in the outer group
- Reference both groups in the ACL rule's src list instead of nesting them
Example fix
// before
{"group:all": ["group:eng", "group:sales"]}
// after
{"group:all": ["a@ex.com", "b@ex.com"]}
// or in the rule:
"src": ["group:eng", "group:sales"] Defensive patterns
Strategy: validation
Validate before calling
func noNestedGroups(groups map[string][]string) bool {
for _, members := range groups {
for _, m := range members { if strings.HasPrefix(m, "group:") { return false } }
}
return true
} Prevention
- Flatten group hierarchies at authoring time — groups contain users only
- Compose groups at the rule level by listing several groups in src
When it happens
Trigger: {"group:a": ["group:b"]} anywhere in the groups map — headscale, like Tailscale SaaS, flattens groups only over users.
Common situations: Trying to build group hierarchies (e.g. group:all containing group:eng and group:sales); migrating from systems that permit nested groups.
Related errors
- group must start with 'group:'
- invalid group member type
- group value must be an array of users
- %w: group %q expected string but got %T
- %w: group %q got string: %q
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/8df0130f6f3661f1.
Report an issue: GitHub.