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
- Make the table value a YAML array of rule maps, even for a single rule.
- Check indentation: each rule must be a list item (leading dash) under inbound/outbound.
- 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
- Always use YAML list syntax (dashes) for inbound/outbound rules.
- Run the config through nebula's config test/parse before deploying.
- Validate YAML structure with a schema (JSON Schema for nebula config) in CI.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unknown protocol %v
- %s rule #%v; %s
- %s rule #%v; only one of port or code should be provided
- %s rule #%v; at least one of host, group, cidr, local_cidr,
- %s rule #%v; proto was not understood; `%s`
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/308461656c45d125.
Report an issue: GitHub.