OpenNHP/opennhp · error

relay: server # instance # missing or invalid port

Error message

relay: server #%d instance #%d missing or invalid port

What it means

normalize requires each instance's port to be a positive integer; this error fires when inst.Port <= 0. TOML maps a missing port to 0, and negative or zero ports are not valid TCP/UDP endpoints, so the entry is rejected.

Solutions

  1. Set port in the flagged [[Servers.Instances]] block to the upstream service's port number (1-65535)
  2. Check the key is exactly `port` and is an unquoted integer (port = 10161, not port = "10161")
  3. If rendering from templates, ensure the port variable is a positive number in the deploy environment

Example fix

// before
[[Servers.Instances]]
host = "10.0.0.5"
port = 0
// 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 inst.Port <= 0 || inst.Port > 65535 {
			return fmt.Errorf("server #%d instance #%d invalid port", i, j)
		}
	}
}

Try / catch

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

Prevention

When it happens

Trigger: The flagged [[Servers.Instances]] block omits port entirely (zero value) or sets port = 0 / a negative number, detected while normalize iterates instances.

Common situations: Port key misspelled (e.g. portNumber) so the field stays 0; template variable for the port unset; operator used a string port that failed to map and left 0; copying a block and forgetting the port.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/relay/config.go:249

		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
			}
		}
		switch c.LoadBalance {
		case "":
			c.LoadBalance = LBWeightedRandom

View on GitHub (pinned to 6e04ca5ff0)