{"record":{"id":"c1d11bcb8697890e","repo":"OpenNHP/opennhp","slug":"cluster-q-instance-d-must-set-either-host-or-i","errorCode":null,"errorMessage":"cluster %q instance #%d: must set either Host or Ip","messagePattern":"cluster %q instance #(.+?): must set either Host or Ip","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/agent/cluster.go","lineNumber":155,"sourceCode":"\t\t\tcfg.Name, cfg.PubKeyBase64)\n\t}\n\tif err := cfg.LoadBalance.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"cluster %q (%s): %w\",\n\t\t\tcfg.Name, cfg.PubKeyBase64, err)\n\t}\n\n\tsc := &ServerCluster{\n\t\tPublicKeyBase64: cfg.PubKeyBase64,\n\t\tName:            cfg.Name,\n\t\tSticky:          cfg.StickyOrDefault(),\n\t\tinstances:       make([]*ServerInstance, 0, len(cfg.Instances)),\n\t}\n\n\tfor i, ic := range cfg.Instances {\n\t\thost := ic.Host\n\t\tip := ic.Ip\n\t\tif host == \"\" && ip == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"cluster %q instance #%d: must set either Host or Ip\",\n\t\t\t\tcfg.Name, i)\n\t\t}\n\t\tif ic.Port <= 0 {\n\t\t\treturn nil, fmt.Errorf(\"cluster %q instance #%d: invalid port %d\",\n\t\t\t\tcfg.Name, i, ic.Port)\n\t\t}\n\t\tpeer := &core.UdpPeer{\n\t\t\tPubKeyBase64: cfg.PubKeyBase64,\n\t\t\tHostname:     host,\n\t\t\tIp:           ip,\n\t\t\tPort:         ic.Port,\n\t\t\tType:         core.NHP_SERVER,\n\t\t\tExpireTime:   cfg.ExpireTime,\n\t\t}\n\t\tdisplayHost := host\n\t\tif displayHost == \"\" {\n\t\t\tdisplayHost = ip\n\t\t}","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/agent/cluster.go#L137-L173","documentation":"buildCluster validates each [[Servers]] instance entry in server.toml before creating a UdpPeer. When an instance defines neither Host nor Ip, there is no address to knock against, so the cluster cannot be built and a descriptive error naming the cluster and instance index is returned. This is a config-validation guard to fail fast at startup instead of producing an unresolvable peer.","triggerScenarios":"Calling buildCluster (directly or via buildTwoInstanceCluster, updateServerPeers, or FindServerClusterFromResource paths in tests) with a cfg.Instances[i] where both Host==\"\" and Ip==\"\".","commonSituations":"Hand-edited server.toml where a [[Servers.Instances]] block has a Port and PubKey but the Host/Ip line was deleted or commented out; template rendering with envsubst where the address variable was empty in the secrets/config template; programmatic Config construction in tests omitting both fields.","solutions":["Set either Host (DNS name) or Ip for every instance in the cluster's [[Servers.Instances]] in server.toml.","Check the rendered config in deploy/configs/ or /etc/opennhp/agent to confirm the address variable (e.g. ${NHP_SERVER_HOST}) was not empty at render time.","If building the Config in code, populate ic.Host or ic.Ip before calling buildCluster.","Run the agent once with debug logging to see which cluster name and instance index failed, then fix that block."],"exampleFix":"// before\n[[Servers.Instances]]\nPort = 10581\nPubKeyBase64 = \"...\"\n\n// after\n[[Servers.Instances]]\nIp = \"10.0.0.5\"   # or Host = \"nhp.example.org\"\nPort = 10581\nPubKeyBase64 = \"...\"","handlingStrategy":"validation","validationCode":"for i, ic := range cfg.Instances {\n    if ic.Host == \"\" && ic.Ip == \"\" {\n        return fmt.Errorf(\"instance %d of cluster %q needs Host or Ip\", i, cfg.Name)\n    }\n}","typeGuard":null,"tryCatchPattern":"clusters, err := buildCluster(cfg)\nif err != nil {\n    return fmt.Errorf(\"load server.toml: %w\", err)\n}","preventionTips":["Lint server.toml at deploy time: every instance must have Host or Ip.","Check rendered templates for empty envsubst variables before shipping configs.","Add a unit test covering an instance with both fields empty."],"tags":["config","validation","go","nhp-agent"],"backgroundTag":"missing-required-config-field","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"}