OpenNHP/opennhp · critical
: no [[Servers]] configured
Error message
%s: no [[Servers]] configured
What it means
clusterconfig.Normalize validates a cluster list and rejects an empty slice outright — at least one [[Servers]] entry is mandatory. The label prefix (consumer label or "cluster") identifies which config section failed validation.
Solutions
- Add at least one [[Servers]] block with PubKeyBase64 to the config file
- Re-run the deploy/config template rendering so [[Servers]] entries are emitted
- Check that the TOML is being read from the expected path (an empty/missing file yields zero clusters)
- Fix indentation/table syntax so the [[Servers]] entries actually parse as array-of-tables
Example fix
// before (server.toml) # [[Servers]] # PubKeyBase64 = "..." // after [[Servers]] Name = "nhp-server-1" PubKeyBase64 = "<base64 server public key>"
Defensive patterns
Strategy: validation
Validate before calling
var clusters []clusterconfig.ClusterConfig
if err := toml.Unmarshal(data, &clusters); err != nil { return err }
if len(clusters) == 0 {
return errors.New("config must define at least one [[Servers]] block")
}
err := clusterconfig.Normalize(clusters, opts) Try / catch
if err := clusterconfig.Normalize(clusters, opts); err != nil {
return fmt.Errorf("cluster config invalid: %w", err)
} Prevention
- Never deploy a rendered config without checking that [[Servers]] blocks exist
- Keep a default non-empty server.toml in the image
- Assert the config template renders at least one cluster in CI
When it happens
Trigger: Calling Normalize with clusters == nil or len(clusters) == 0, typically from normalizeAndExpand/normalizeClusters/updateServerPeers when a TOML file contained no [[Servers]] array entries.
Common situations: config/server.toml with the [[Servers]] table commented out or deleted; a template rendering that dropped all server entries; parsing a file with only top-level keys and no clusters array.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- server peer config invalid on initial load
- cluster : missing publicKeyBase64
- cluster ( ): no instances configured
- cluster instance # : must set either Host or Ip
- cluster instance # : invalid port
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/7e447f665aab8500.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/common/clusterconfig/clusterconfig.go:140
// auto-upgrade in-place. After it returns, every cluster has at least
// one Instances entry and a normalised LoadBalance. Returned errors are
// surfaced to the operator at load time; warnings (deprecation,
// recoverable inconsistencies) go through deprecate.
//
// 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) {View on GitHub (pinned to 6e04ca5ff0)