OpenNHP/opennhp · error

cluster ( )

Error message

cluster %q (%s): %w

What it means

buildCluster wraps cfg.LoadBalance.Validate()'s error as "cluster %q (%s): %w" when the cluster's load-balance setting is invalid. The wrapped error names the unacceptable scheme, and this wrapper adds the cluster name and public key for identification.

Solutions

  1. Set loadBalance to one of the exact supported values (check the LoadBalance type/Validate in endpoints/agent)
  2. Check the wrapped error text for the offending value and correct the casing/spelling
  3. Omit the field to use the default scheme if one exists
  4. Add the accepted values to config documentation/templates to prevent recurrence

Example fix

// before
[[clusters]]
name = "nhp-server"
publicKeyBase64 = "abc..."
loadBalance = "least-connections"   # unsupported
// after
[[clusters]]
name = "nhp-server"
publicKeyBase64 = "abc..."
loadBalance = "roundRobin"          # supported scheme
Defensive patterns

Strategy: validation

Validate before calling

var lb LoadBalance
if err := lb.UnmarshalText([]byte(cfg.LoadBalance)); err != nil {
	return fmt.Errorf("cluster %q: bad loadBalance %q (allowed: %v)", cfg.Name, cfg.LoadBalance, lb.Allowed())
}

Try / catch

cl, err := buildCluster(cfg)
if err != nil {
	var le *LoadBalanceError
	if errors.As(err, &le) { /* fix the scheme value */ }
}

Prevention

When it happens

Trigger: A cluster config sets loadBalance (or similar field) to a string/enum outside the supported set (e.g. "round-robin" vs supported values like random/roundRobin/priority), so LoadBalance.Validate() returns an error during updateServerPeers.

Common situations: Typo in the load-balance mode value; using a scheme removed in a newer version; case-sensitivity mismatch ("Round-Robin" vs "roundRobin"); copying config from a different project with different scheme names.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/agent/cluster.go:140

		}
	}
	return nil
}

// buildCluster turns a parsed ClusterConfig into a runtime cluster.
// The returned cluster's representativePeer is NOT yet registered on a
// device — callers (updateServerPeers) are responsible for that, so
// they can also handle peer removal on reload.
func buildCluster(cfg *ClusterConfig) (*ServerCluster, error) {
	if cfg.PubKeyBase64 == "" {
		return nil, fmt.Errorf("cluster %q: missing publicKeyBase64", cfg.Name)
	}
	if len(cfg.Instances) == 0 {
		return nil, fmt.Errorf("cluster %q (%s): no instances configured",
			cfg.Name, cfg.PubKeyBase64)
	}
	if err := cfg.LoadBalance.Validate(); err != nil {
		return nil, fmt.Errorf("cluster %q (%s): %w",
			cfg.Name, cfg.PubKeyBase64, err)
	}

	sc := &ServerCluster{
		PublicKeyBase64: cfg.PubKeyBase64,
		Name:            cfg.Name,
		Sticky:          cfg.StickyOrDefault(),
		instances:       make([]*ServerInstance, 0, len(cfg.Instances)),
	}

	for i, ic := range cfg.Instances {
		host := ic.Host
		ip := ic.Ip
		if host == "" && ip == "" {
			return nil, fmt.Errorf("cluster %q instance #%d: must set either Host or Ip",
				cfg.Name, i)
		}
		if ic.Port <= 0 {

View on GitHub (pinned to 6e04ca5ff0)