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

  1. Flatten the nested group: list the individual user emails directly in the outer group
  2. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/8df0130f6f3661f1. Report an issue: GitHub.