OpenNHP/opennhp · error
: [[Servers]][ ] is nil
Error message
%s: [[Servers]][%d] is nil
What it means
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.
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
Example fix
// before
clusters := make([]*clusterconfig.ClusterConfig, len(raw))
normalize(clusters)
// after
var clusters []*clusterconfig.ClusterConfig
for _, r := range raw {
if c := decode(r); c != nil {
clusters = append(clusters, c)
}
}
normalize(clusters) Defensive patterns
Strategy: type-guard
Validate before calling
for i, c := range clusters {
if c == nil { return fmt.Errorf("cluster %d is nil before Normalize", i) }
}
err := clusterconfig.Normalize(clusters, opts) Type guard
func allClustersSet(cs []*clusterconfig.ClusterConfig) bool {
for _, c := range cs { if c == nil { return false } }
return len(cs) > 0
} Try / catch
if err := clusterconfig.Normalize(clusters, opts); err != nil {
if strings.Contains(err.Error(), "is nil") { log.Fatalf("loader produced nil cluster entry: %v", err) }
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cluster : missing publicKeyBase64
- cluster ( ): no instances configured
- cluster instance # : must set either Host or Ip
- cluster instance # : invalid port
- relay: privateKeyBase64 must be set in config
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/7f8f7b431dd3ebd4.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/common/clusterconfig/clusterconfig.go:144
//
// deprecate is a callback so callers control how the warning is
// surfaced — production code passes log.Warning; tests can capture
// invocations for assertions. A nil deprecate is treated as no-op.
func Normalize(clusters []*ClusterConfig, opts Options, deprecate func(string, ...any)) error {
if deprecate == nil {
deprecate = func(string, ...any) {}
}
label := opts.ConsumerLabel
if label == "" {
label = "cluster"
}
if len(clusters) == 0 {
return fmt.Errorf("%s: no [[Servers]] configured", label)
}
for i, c := range clusters {
if c == nil {
return fmt.Errorf("%s: [[Servers]][%d] is nil", label, i)
}
if c.PubKeyBase64 == "" {
return fmt.Errorf("%s: [[Servers]][%d] missing PubKeyBase64", label, i)
}
if opts.RequireName {
if c.Name == "" {
return fmt.Errorf("%s: [[Servers]][%d] (%s) missing Name — clusters are referenced from resource.toml by Name",
label, i, c.PubKeyBase64)
}
if len(c.Name) > NameMaxLen {
return fmt.Errorf("%s: [[Servers]][%d] Name %q exceeds %d chars",
label, i, c.Name, NameMaxLen)
}
if !clusterNameRegex.MatchString(c.Name) {
return fmt.Errorf("%s: [[Servers]][%d] Name %q invalid — allowed chars: [a-zA-Z0-9._-]",
label, i, c.Name)
}
}View on GitHub (pinned to 6e04ca5ff0)