slackhq/nebula · error

%s failed to parse, should be an array of rules

Error message

%s failed to parse, should be an array of rules

What it means

AddFirewallRulesFromConfig expects the YAML value for a firewall table (inbound/outbound) to be an array of rule maps. If the decoded value is present but not a []any (e.g. it's a map or scalar), this error is returned naming the offending table.

Source

Thrown at firewall.go:335

	return "SHA:" + f.GetRuleHash() + ",FNV:" + strconv.FormatUint(uint64(f.GetRuleHashFNV()), 10)
}

func AddFirewallRulesFromConfig(l *slog.Logger, inbound bool, c *config.C, fw FirewallInterface) error {
	var table string
	if inbound {
		table = "firewall.inbound"
	} else {
		table = "firewall.outbound"
	}

	r := c.Get(table)
	if r == nil {
		return nil
	}

	rs, ok := r.([]any)
	if !ok {
		return fmt.Errorf("%s failed to parse, should be an array of rules", table)
	}

	for i, t := range rs {
		r, err := convertRule(l, t, table, i)
		if err != nil {
			return fmt.Errorf("%s rule #%v; %s", table, i, err)
		}

		if r.Code != "" && r.Port != "" {
			return fmt.Errorf("%s rule #%v; only one of port or code should be provided", table, i)
		}

		if r.Host == "" && len(r.Groups) == 0 && r.Cidr == "" && r.LocalCidr == "" && r.CAName == "" && r.CASha == "" {
			return fmt.Errorf("%s rule #%v; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided", table, i)
		}

		var sPort, errPort string
		if r.Code != "" {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Make the table value a YAML array of rule maps, even for a single rule.
  2. Check indentation: each rule must be a list item (leading dash) under inbound/outbound.
  3. If the table should be empty, remove the key entirely rather than setting it to a non-list value.

Example fix

// before (config)
firewall:
  inbound:
    port: 443
    proto: tcp
// after
firewall:
  inbound:
    - port: 443
      proto: tcp
Defensive patterns

Strategy: validation

Validate before calling

func validateFirewallTable(cfg map[string]any, table string) error {
    v, ok := cfg["firewall"].(map[string]any)
    if !ok { return nil }
    t, present := v[table]
    if !present || t == nil { return nil }
    if _, ok := t.([]any); !ok {
        return fmt.Errorf("firewall.%s must be a list of rules", table)
    }
    return nil
}

Type guard

func isRuleList(v any) bool {
    _, ok := v.([]any)
    return ok
}

Try / catch

err := fw.AddFirewallRulesFromConfig(l, "inbound", rawInbound)
if err != nil {
    return fmt.Errorf("invalid firewall config: %w", err)
}

Prevention

When it happens

Trigger: Calling AddFirewallRulesFromConfig (directly or via NewFirewallFromConfig) where the config's "firewall.inbound" or "firewall.outbound" is defined but is not a YAML list — e.g. "inbound: {}" or "inbound: true".

Common situations: YAML indentation mistakes that turn the list of rules into a nested map, copy-pasting a single rule object instead of a one-element list, or wrong key nesting under the firewall stanza.

Understand the failure class

Related errors


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