slackhq/nebula · error
%s rule #%v; %s
Error message
%s rule #%v; %s
What it means
While iterating rules in a firewall table, AddFirewallRulesFromConfig calls convertRule to decode each entry into a firewall rule. If convertRule fails (bad types, missing fields, unparseable values), the error is wrapped as "<table> rule #<index>; <detail>" so the offending rule can be located in the config.
Source
Thrown at firewall.go:341
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 != "" {
errPort = "code"
sPort = r.Code
} else {
errPort = "port"
sPort = r.Port
}View on GitHub (pinned to dd8f660c0a)
Solutions
- Read the wrapped detail after "rule #N;" — it names the exact field and conversion failure.
- Quote port, code, and ca_sha values as strings in YAML to avoid type coercion.
- Ensure each rule entry is a mapping (key: value pairs), not a scalar or list.
Example fix
// before (config) - port: [80, 443] proto: tcp // after - port: 443 proto: tcp // (or two separate rules, one per port)
Defensive patterns
Strategy: validation
Validate before calling
func precheckRules(table string, rules []map[string]any) error {
for i, r := range rules {
for k, v := range r {
switch k {
case "port", "code", "ca_name", "ca_sha", "host", "proto", "cidr", "local_cidr":
if _, ok := v.(string); !ok {
return fmt.Errorf("%s rule #%d: field %q must be a quoted string, got %T", table, i, k, v)
}
case "groups":
if _, ok := v.([]any); !ok && v != nil {
return fmt.Errorf("%s rule #%d: groups must be a list", table, i)
}
}
}
}
return nil
} Try / catch
if err := fw.AddFirewallRulesFromConfig(l, "inbound", rules); err != nil {
// err looks like "inbound rule #2; ..." — surface it with config file context
return fmt.Errorf("firewall config %s: %w", configFile, err)
} Prevention
- Quote all scalar rule values (port, code, ca_sha) in YAML.
- Keep each rule a flat mapping with known keys only.
- Test firewall config changes on one node before fleet rollout.
When it happens
Trigger: Any rule object in inbound/outbound whose fields cannot be converted — e.g. "port: [80]" as a list instead of a string, non-string ca_sha, or a rule entry that is not a map.
Common situations: YAML type coercion surprises (unquoted values that decode as ints/bools), typos in field structure, or programmatic config generation emitting wrong JSON/YAML types.
Related errors
- %s failed to parse, should be an array of rules
- ErrInvalidLocalIP
- could not parse rule
- unknown protocol %v
- %s rule #%v; only one of port or code should be provided
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/21100d3af7bf82b1.
Report an issue: GitHub.