{"record":{"id":"2b3f4c1817b0358d","repo":"slackhq/nebula","slug":"could-not-parse-rule","errorCode":null,"errorMessage":"could not parse rule","messagePattern":"could not parse rule","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"firewall.go","lineNumber":950,"sourceCode":"\ntype rule struct {\n\tPort      string\n\tCode      string\n\tProto     string\n\tHost      string\n\tGroups    []string\n\tCidr      string\n\tLocalCidr string\n\tCAName    string\n\tCASha     string\n}\n\nfunc convertRule(l *slog.Logger, p any, table string, i int) (rule, error) {\n\tr := rule{}\n\n\tm, ok := p.(map[string]any)\n\tif !ok {\n\t\treturn r, errors.New(\"could not parse rule\")\n\t}\n\n\ttoString := func(k string, m map[string]any) string {\n\t\tv, ok := m[k]\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn fmt.Sprintf(\"%v\", v)\n\t}\n\n\tr.Port = toString(\"port\", m)\n\tr.Code = toString(\"code\", m)\n\tr.Proto = toString(\"proto\", m)\n\tr.Host = toString(\"host\", m)\n\tr.Cidr = toString(\"cidr\", m)\n\tr.LocalCidr = toString(\"local_cidr\", m)\n\tr.CAName = toString(\"ca_name\", m)\n\tr.CASha = toString(\"ca_sha\", m)","sourceCodeStart":932,"sourceCodeEnd":968,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/firewall.go#L932-L968","documentation":"Not a sentinel: an anonymous error created inline in convertRule (firewall.go:950) with message \"could not parse rule\". convertRule converts a raw config value (from the firewall rules section of the YAML/JSON config) into a rule struct, and returns this error when the value is not a map[string]any — i.e. the rule entry in the config is structurally invalid before any per-field parsing happens. It aborts firewall rule loading so the operator knows the config is malformed.","triggerScenarios":"Calling Firewall reload/add-firewall-rule config paths where an entry in the firewall.inbound/outbound list is not a mapping/object — e.g. it's a string, number, array, or null — so the type assertion p.(map[string]any) at firewall.go:950 fails.","commonSituations":"YAML indentation mistakes that turn a rule into a scalar or list; quoting a whole rule as a string; mixing list and map syntax; programmatically supplied rule JSON with wrong shape (array instead of object); config hot-reload picking up a partially written file.","solutions":["Open the config at the reported table/index and make the rule entry an object/map with keys like port, proto, host, group, cidr","Fix YAML indentation so each rule is a map element of the inbound/outbound list, not a scalar","If rules are provided programmatically, pass map[string]any (decoded JSON object) per rule, not strings or arrays","Run a config validation/dry-run before reload to catch malformed rule entries early"],"exampleFix":"// before (malformed YAML: rule collapsed to a string)\nfirewall:\n  inbound:\n    - \"port: 443 proto: any\"\n// after\nfirewall:\n  inbound:\n    - port: 443\n      proto: any\n      host: web","handlingStrategy":"validation","validationCode":"// validate every firewall rule entry is an object before handing config to the firewall\nfor i, r := range cfg.Firewall.Inbound {\n    if _, ok := r.(map[string]any); !ok {\n        return fmt.Errorf(\"firewall.inbound[%d] must be a mapping, got %T\", i, r)\n    }\n}","typeGuard":"func isRuleMap(v any) bool {\n    _, ok := v.(map[string]any)\n    return ok\n}","tryCatchPattern":"r, err := convertRule(logger, raw, \"inbound\", i)\nif err != nil {\n    if err.Error() == \"could not parse rule\" {\n        // raw entry is not a map: log the index and offending value, then abort reload\n    }\n}","preventionTips":["Keep each firewall rule as a properly indented YAML mapping inside the inbound/outbound lists","Lint the config (yaml parser + schema check) before every reload or hot-apply","When generating rules programmatically, emit map[string]any objects, never serialized strings","Beware partially-written config files during hot reload; write atomically (temp file + rename)"],"tags":["configuration","yaml","firewall-rules","parse-error"],"backgroundTag":"invalid-firewall-rule-config","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"}