{"record":{"id":"1f024441f6ce0299","repo":"slackhq/nebula","slug":"s-rule-v-only-one-of-port-or-code-should-be-pr","errorCode":null,"errorMessage":"%s rule #%v; only one of port or code should be provided","messagePattern":"(.+?) rule #(.+?); only one of port or code should be provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"firewall.go","lineNumber":345,"sourceCode":"\n\tr := c.Get(table)\n\tif r == nil {\n\t\treturn nil\n\t}\n\n\trs, ok := r.([]any)\n\tif !ok {\n\t\treturn fmt.Errorf(\"%s failed to parse, should be an array of rules\", table)\n\t}\n\n\tfor i, t := range rs {\n\t\tr, err := convertRule(l, t, table, i)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s rule #%v; %s\", table, i, err)\n\t\t}\n\n\t\tif r.Code != \"\" && r.Port != \"\" {\n\t\t\treturn fmt.Errorf(\"%s rule #%v; only one of port or code should be provided\", table, i)\n\t\t}\n\n\t\tif r.Host == \"\" && len(r.Groups) == 0 && r.Cidr == \"\" && r.LocalCidr == \"\" && r.CAName == \"\" && r.CASha == \"\" {\n\t\t\treturn fmt.Errorf(\"%s rule #%v; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided\", table, i)\n\t\t}\n\n\t\tvar sPort, errPort string\n\t\tif r.Code != \"\" {\n\t\t\terrPort = \"code\"\n\t\t\tsPort = r.Code\n\t\t} else {\n\t\t\terrPort = \"port\"\n\t\t\tsPort = r.Port\n\t\t}\n\n\t\tvar proto uint8\n\t\tvar startPort, endPort int32\n\t\tswitch r.Proto {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/firewall.go#L327-L363","documentation":"A firewall rule may specify a port (for tcp/udp) or a code (for icmp), but not both. AddFirewallRulesFromConfig rejects rules where both Code and Port are non-empty so the resulting rule is unambiguous.","triggerScenarios":"A rule in inbound/outbound that sets both \"port\" and \"code\" fields, e.g. {port: \"443\", code: \"8\", proto: icmp}.","commonSituations":"Copy-pasting a rule template and forgetting to delete the unused port/code field, or merging rules from different examples.","solutions":["Remove the \"port\" field if the rule is an ICMP rule identified by \"code\".","Remove the \"code\" field if the rule is tcp/udp identified by \"port\".","Split into two rules if you genuinely need both a port-based and a code-based rule."],"exampleFix":"// before (config)\n- port: 443\n  code: 8\n  proto: tcp\n// after\n- port: 443\n  proto: tcp","handlingStrategy":"validation","validationCode":"func checkPortXorCode(rule map[string]any, table string, i int) error {\n    _, hasPort := rule[\"port\"]\n    _, hasCode := rule[\"code\"]\n    if hasPort && hasCode {\n        return fmt.Errorf(\"%s rule #%d: only one of port or code\", table, i)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := fw.AddFirewallRulesFromConfig(l, table, rules); err != nil {\n    if strings.Contains(err.Error(), \"only one of port or code\") {\n        log.Fatalf(\"fix rule: keep port for tcp/udp, code for icmp: %v\", err)\n    }\n    return err\n}","preventionTips":["Use distinct rule templates for tcp/udp (port) and icmp (code) rules.","When editing rules by hand, delete the unused field, don't just comment it out in generated YAML.","Lint firewall rules for mutually exclusive fields in CI."],"tags":["firewall","configuration","validation"],"backgroundTag":"conflicting-rule-fields","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}