slackhq/nebula · error

only one of group or groups should be defined, both provided

Error message

only one of group or groups should be defined, both provided

What it means

In firewall rule normalization, the config allows either the singular 'group' or plural 'groups' field. Providing both is ambiguous, so the rule is rejected with this error rather than merging them. This lives in the rule-translation step that converts YAML firewall rule structs into internal rules.

Source

Thrown at firewall.go:1004

		switch reflect.TypeOf(rg).Kind() {
		case reflect.Slice:
			v := reflect.ValueOf(rg)
			r.Groups = make([]string, v.Len())
			for i := 0; i < v.Len(); i++ {
				r.Groups[i] = v.Index(i).Interface().(string)
			}
		case reflect.String:
			r.Groups = []string{rg.(string)}
		default:
			r.Groups = []string{fmt.Sprintf("%v", rg)}
		}
	}

	//flatten group vs groups
	if singleGroup != "" {
		// Check if we have both groups and group provided in the rule config
		if len(r.Groups) > 0 {
			return r, fmt.Errorf("only one of group or groups should be defined, both provided")
		}
		r.Groups = []string{singleGroup}
	}

	return r, nil
}

// sanity returns an error if the rule would be evaluated in a way that would short-circuit a configured check on a wildcard value
// rules are evaluated as "port AND proto AND (ca_sha OR ca_name) AND (host OR group OR groups OR cidr) AND local_cidr"
func (r *rule) sanity() error {
	//port, proto, local_cidr are AND, no need to check here
	//ca_sha and ca_name don't have a wildcard value, no need to check here
	groupsEmpty := len(r.Groups) == 0
	hostEmpty := r.Host == ""
	cidrEmpty := r.Cidr == ""

	if (groupsEmpty && hostEmpty && cidrEmpty) == true {
		return nil //no content!

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Keep only the 'groups' list and move the singular value into it
  2. Or keep only 'group' if a single group suffices
  3. Search your config for rules containing both keys

Example fix

// before
group: prod
groups:
  - prod
  - staging
// after
groups:
  - prod
  - staging
Defensive patterns

Strategy: validation

Validate before calling

func checkGroupConflict(r FirewallRuleConfig) error {
    if r.Group != "" && len(r.Groups) > 0 {
        return fmt.Errorf("rule defines both group and groups")
    }
    return nil
}

Type guard

func hasGroupConflict(group string, groups []string) bool {
    return group != "" && len(groups) > 0
}

Try / catch

if err := loadFirewallConfig(cfg); err != nil {
    if strings.Contains(err.Error(), "only one of group or groups") {
        return fmt.Errorf("fix rule config: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A single firewall rule in the YAML/config struct defines both 'group: foo' and 'groups: [foo, bar]'.

Common situations: Merged config files where one fragment used group and another used groups; copy-paste from examples mixing syntax; templating that appends a singular group to a rule already carrying a list.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/443add904fe40221. Report an issue: GitHub.