OpenNHP/opennhp · error

relay: server # instance # missing host

Error message

relay: server #%d instance #%d missing host

What it means

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.

Solutions

  1. Set host in the flagged [[Servers.Instances]] block to the upstream server's hostname or IP
  2. If rendering from templates, verify the host variable is populated in the deploy environment
  3. Remove instance blocks that are placeholders and not actually intended

Example fix

// before
[[Servers.Instances]]
host = ""
port = 10161
// after
[[Servers.Instances]]
host = "10.0.0.5"
port = 10161
Defensive patterns

Strategy: validation

Validate before calling

for i, s := range cfg.Servers {
	for j, inst := range s.Instances {
		if strings.TrimSpace(inst.Host) == "" {
			return fmt.Errorf("server #%d instance #%d host empty", i, j)
		}
	}
}

Type guard

func hostSet(inst relay.Instance) bool { return strings.TrimSpace(inst.Host) != "" }

Try / catch

if err := cfg.Normalize(); err != nil {
	if strings.Contains(err.Error(), "missing host") {
		return fmt.Errorf("set host in every [[Servers.Instances]]: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/315f604131f2003b. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/relay/config.go:246

		if c.PubKeyBase64 == "" {
			return fmt.Errorf("relay: server #%d missing publicKeyBase64", i)
		}
		fp, err := utils.PubKeyFingerprintFromBase64(c.PubKeyBase64)
		if err != nil {
			return fmt.Errorf("relay: server #%d publicKeyBase64 invalid: %w", i, err)
		}
		if dup, ok := seenFP[fp]; ok {
			return fmt.Errorf("relay: server #%d and #%d share the same publicKeyBase64 (fingerprint %s)", dup, i, fp)
		}
		seenFP[fp] = i

		if len(c.Instances) == 0 {
			return fmt.Errorf("relay: server #%d (fingerprint %s) has no [[Servers.Instances]]", i, fp)
		}
		for j := range c.Instances {
			inst := &c.Instances[j]
			if inst.Host == "" {
				return fmt.Errorf("relay: server #%d instance #%d missing host", i, j)
			}
			if inst.Port <= 0 {
				return fmt.Errorf("relay: server #%d instance #%d missing or invalid port", i, j)
			}
			addr := fmt.Sprintf("%s:%d", inst.Host, inst.Port)
			// Scope to this server's pubkey: same identity reusing an
			// address is the copy-paste error we reject; a sibling
			// identity on the same address is allowed (see seenAddr docs).
			addrKey := fp + "@" + addr
			if dup, ok := seenAddr[addrKey]; ok {
				return fmt.Errorf("relay: server #%d instance #%d address %s already claimed by server #%d instance #%d under the same publicKeyBase64 (fingerprint %s)",
					i, j, addr, dup.server, dup.instance, fp)
			}
			seenAddr[addrKey] = addrOrigin{server: i, instance: j}
			if inst.Weight <= 0 {
				inst.Weight = 1
			}
		}

View on GitHub (pinned to 6e04ca5ff0)