{"record":{"id":"315f604131f2003b","repo":"OpenNHP/opennhp","slug":"relay-server-d-instance-d-missing-host","errorCode":null,"errorMessage":"relay: server #%d instance #%d missing host","messagePattern":"relay: server #(.+?) instance #(.+?) missing host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/relay/config.go","lineNumber":246,"sourceCode":"\t\tif c.PubKeyBase64 == \"\" {\n\t\t\treturn fmt.Errorf(\"relay: server #%d missing publicKeyBase64\", i)\n\t\t}\n\t\tfp, err := utils.PubKeyFingerprintFromBase64(c.PubKeyBase64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"relay: server #%d publicKeyBase64 invalid: %w\", i, err)\n\t\t}\n\t\tif dup, ok := seenFP[fp]; ok {\n\t\t\treturn fmt.Errorf(\"relay: server #%d and #%d share the same publicKeyBase64 (fingerprint %s)\", dup, i, fp)\n\t\t}\n\t\tseenFP[fp] = i\n\n\t\tif len(c.Instances) == 0 {\n\t\t\treturn fmt.Errorf(\"relay: server #%d (fingerprint %s) has no [[Servers.Instances]]\", i, fp)\n\t\t}\n\t\tfor j := range c.Instances {\n\t\t\tinst := &c.Instances[j]\n\t\t\tif inst.Host == \"\" {\n\t\t\t\treturn fmt.Errorf(\"relay: server #%d instance #%d missing host\", i, j)\n\t\t\t}\n\t\t\tif inst.Port <= 0 {\n\t\t\t\treturn fmt.Errorf(\"relay: server #%d instance #%d missing or invalid port\", i, j)\n\t\t\t}\n\t\t\taddr := fmt.Sprintf(\"%s:%d\", inst.Host, inst.Port)\n\t\t\t// Scope to this server's pubkey: same identity reusing an\n\t\t\t// address is the copy-paste error we reject; a sibling\n\t\t\t// identity on the same address is allowed (see seenAddr docs).\n\t\t\taddrKey := fp + \"@\" + addr\n\t\t\tif dup, ok := seenAddr[addrKey]; ok {\n\t\t\t\treturn fmt.Errorf(\"relay: server #%d instance #%d address %s already claimed by server #%d instance #%d under the same publicKeyBase64 (fingerprint %s)\",\n\t\t\t\t\ti, j, addr, dup.server, dup.instance, fp)\n\t\t\t}\n\t\t\tseenAddr[addrKey] = addrOrigin{server: i, instance: j}\n\t\t\tif inst.Weight <= 0 {\n\t\t\t\tinst.Weight = 1\n\t\t\t}\n\t\t}","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/relay/config.go#L228-L264","documentation":"normalize requires every instance to have a non-empty host; this error reports the server and instance indices. Without a host the relay has no address to forward upstream traffic to, so the instance entry is invalid.","triggerScenarios":"The Nth [[Servers.Instances]] block omits the host key or sets it to an empty string (\"\"), e.g. host = \"\" or a template variable that rendered empty during normalize.","commonSituations":"Template rendering with an unset env var produced host = \"\"; operator deleted the host line accidentally; copy-pasted an instance block and blanked the host intending to fill it later.","solutions":["Set host in the flagged [[Servers.Instances]] block to the upstream server's hostname or IP","If rendering from templates, verify the host variable is populated in the deploy environment","Remove instance blocks that are placeholders and not actually intended"],"exampleFix":"// before\n[[Servers.Instances]]\nhost = \"\"\nport = 10161\n// after\n[[Servers.Instances]]\nhost = \"10.0.0.5\"\nport = 10161","handlingStrategy":"validation","validationCode":"for i, s := range cfg.Servers {\n\tfor j, inst := range s.Instances {\n\t\tif strings.TrimSpace(inst.Host) == \"\" {\n\t\t\treturn fmt.Errorf(\"server #%d instance #%d host empty\", i, j)\n\t\t}\n\t}\n}","typeGuard":"func hostSet(inst relay.Instance) bool { return strings.TrimSpace(inst.Host) != \"\" }","tryCatchPattern":"if err := cfg.Normalize(); err != nil {\n\tif strings.Contains(err.Error(), \"missing host\") {\n\t\treturn fmt.Errorf(\"set host in every [[Servers.Instances]]: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Fail template rendering when the host variable is empty instead of emitting host = \"\"","Validate rendered configs in CI before deploy","Use hostnames/IPs from a single source of truth to avoid blanks"],"tags":["relay","config","validation","host"],"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"}