slackhq/nebula · error

groups specified as , but host=any will match any host…

Error message

groups specified as %s, but host=any will match any host, regardless of groups

What it means

host: any matches every host regardless of groups, so specifying groups together with host 'any' is contradictory — the groups can never restrict anything. The rule translator rejects the rule to prevent a false sense of scoping.

Solutions

  1. Remove the groups field if you really want to match any host
  2. Or replace host: any with the intended host name if you want groups to apply

Example fix

// before
host: any
groups: [prod]
// after
host: any
Defensive patterns

Strategy: validation

Validate before calling

func checkHostAnyRule(r FirewallRuleConfig) error {
    if r.Host == "any" && len(r.Groups) > 0 {
        return fmt.Errorf("host=any cannot be combined with groups")
    }
    return nil
}

Type guard

func isHostAnyConsistent(host string, groups []string) bool {
    return host != "any" || len(groups) == 0
}

Try / catch

if err := loadFirewallConfig(cfg); err != nil {
    if strings.Contains(err.Error(), "host=any will match any host") {
        return fmt.Errorf("redundant selectors with host=any: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A rule with host: any and a non-empty groups list during rule translation (e.g. host: any, groups: [prod]).

Common situations: Author thinking host=any means 'any host in these groups'; converting a host-specific rule to any and forgetting to drop groups.

Related errors


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

Appendix: source

Thrown at firewall.go:1032

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!
	}

	groupsHasAny := slices.Contains(r.Groups, "any")
	if groupsHasAny && len(r.Groups) > 1 {
		return fmt.Errorf("groups spec [%s] contains the group '\"any\". This rule will ignore the other groups specified", r.Groups)
	}

	if r.Host == "any" {
		if !groupsEmpty {
			return fmt.Errorf("groups specified as %s, but host=any will match any host, regardless of groups", r.Groups)
		}

		if !cidrEmpty {
			return fmt.Errorf("cidr specified as %s, but host=any will match any host, regardless of cidr", r.Cidr)
		}
	}

	if groupsHasAny {
		if !hostEmpty && r.Host != "any" {
			return fmt.Errorf("groups spec [%s] contains the group '\"any\". This rule will ignore the specified host %s", r.Groups, r.Host)
		}
		if !cidrEmpty {
			return fmt.Errorf("groups spec [%s] contains the group '\"any\". This rule will ignore the specified cidr %s", r.Groups, r.Cidr)
		}
	}

	if r.Code != "" {
		return fmt.Errorf("code specified as [%s]. Support for 'code' will be dropped in a future release, as it has never been functional", r.Code)

View on GitHub (pinned to dd8f660c0a)