{"record":{"id":"7f8f7b431dd3ebd4","repo":"OpenNHP/opennhp","slug":"s-servers-d-is-nil","errorCode":null,"errorMessage":"%s: [[Servers]][%d] is nil","messagePattern":"(.+?): \\[\\[Servers\\]\\]\\[(.+?)\\] is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/common/clusterconfig/clusterconfig.go","lineNumber":144,"sourceCode":"//\n// deprecate is a callback so callers control how the warning is\n// surfaced — production code passes log.Warning; tests can capture\n// invocations for assertions. A nil deprecate is treated as no-op.\nfunc Normalize(clusters []*ClusterConfig, opts Options, deprecate func(string, ...any)) error {\n\tif deprecate == nil {\n\t\tdeprecate = func(string, ...any) {}\n\t}\n\tlabel := opts.ConsumerLabel\n\tif label == \"\" {\n\t\tlabel = \"cluster\"\n\t}\n\n\tif len(clusters) == 0 {\n\t\treturn fmt.Errorf(\"%s: no [[Servers]] configured\", label)\n\t}\n\tfor i, c := range clusters {\n\t\tif c == nil {\n\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] is nil\", label, i)\n\t\t}\n\t\tif c.PubKeyBase64 == \"\" {\n\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] missing PubKeyBase64\", label, i)\n\t\t}\n\t\tif opts.RequireName {\n\t\t\tif c.Name == \"\" {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] (%s) missing Name — clusters are referenced from resource.toml by Name\",\n\t\t\t\t\tlabel, i, c.PubKeyBase64)\n\t\t\t}\n\t\t\tif len(c.Name) > NameMaxLen {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] Name %q exceeds %d chars\",\n\t\t\t\t\tlabel, i, c.Name, NameMaxLen)\n\t\t\t}\n\t\t\tif !clusterNameRegex.MatchString(c.Name) {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] Name %q invalid — allowed chars: [a-zA-Z0-9._-]\",\n\t\t\t\t\tlabel, i, c.Name)\n\t\t\t}\n\t\t}","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/common/clusterconfig/clusterconfig.go#L126-L162","documentation":"During Normalize's per-cluster loop, a nil *ClusterConfig entry in the clusters slice is rejected. A nil entry carries no public key or address and cannot become a peer, so normalization aborts with the index of the offending entry.","triggerScenarios":"Calling Normalize where clusters[i] == nil for some i — e.g. a slice pre-allocated with make([]*ClusterConfig, n) and never fully populated, or a loader appending nil for malformed TOML blocks.","commonSituations":"Programmatic construction of the cluster slice sized but not filled; a custom config loader that appends nil on decode errors instead of skipping; test harnesses building partial fixtures.","solutions":["Populate every element of the clusters slice before calling Normalize","Filter out nil entries in the loader: append only successfully decoded *ClusterConfig values","Fix the code path that created the slice so it grows with append instead of a fixed make(..., n)","Ensure the TOML parser surfaces malformed [[Servers]] blocks as errors rather than nil entries"],"exampleFix":"// before\nclusters := make([]*clusterconfig.ClusterConfig, len(raw))\nnormalize(clusters)\n// after\nvar clusters []*clusterconfig.ClusterConfig\nfor _, r := range raw {\n    if c := decode(r); c != nil {\n        clusters = append(clusters, c)\n    }\n}\nnormalize(clusters)","handlingStrategy":"type-guard","validationCode":"for i, c := range clusters {\n    if c == nil { return fmt.Errorf(\"cluster %d is nil before Normalize\", i) }\n}\nerr := clusterconfig.Normalize(clusters, opts)","typeGuard":"func allClustersSet(cs []*clusterconfig.ClusterConfig) bool {\n    for _, c := range cs { if c == nil { return false } }\n    return len(cs) > 0\n}","tryCatchPattern":"if err := clusterconfig.Normalize(clusters, opts); err != nil {\n    if strings.Contains(err.Error(), \"is nil\") { log.Fatalf(\"loader produced nil cluster entry: %v\", err) }\n    return err\n}","preventionTips":["Build slices with append rather than make([]*T, n) + index assignment","Skip (and log) undecodable TOML blocks instead of appending nil","Add a pre-Normalize nil sweep in config loaders"],"tags":["config","validation","nil"],"backgroundTag":"null-argument","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}