OpenNHP/opennhp · error

cluster ( ): no instances configured

Error message

cluster %q (%s): no instances configured

What it means

buildCluster returns "cluster %q (%s): no instances configured" when a ClusterConfig has a valid public key but zero instances. A cluster with no endpoints has nothing to knock against, so it is rejected rather than producing a cluster that always fails at knock time.

Solutions

  1. Add at least one reachable server instance URL (udp://host:port) to the cluster's instances list
  2. Check the rendered config for an empty instances value caused by an unset template variable
  3. Verify the instances field name spelling matches the parser so entries are not silently dropped
  4. Fail fast in config loading with the cluster name, which this error already provides — fix the offending cluster block

Example fix

// before
[[clusters]]
name = "nhp-server"
publicKeyBase64 = "abc..."
// after
[[clusters]]
name = "nhp-server"
publicKeyBase64 = "abc..."
instances = ["udp://nhp1.example.com:5555", "udp://nhp2.example.com:5555"]
Defensive patterns

Strategy: validation

Validate before calling

if len(cfg.Instances) == 0 {
	return fmt.Errorf("cluster %q: at least one instance URL required", cfg.Name)
}
for _, u := range cfg.Instances {
	if _, err := url.Parse(u); err != nil { return err }
}

Try / catch

cl, err := buildCluster(cfg)
if err != nil {
	return fmt.Errorf("cluster %s rejected: %w", cfg.Name, err)
}

Prevention

When it happens

Trigger: A clusters entry defines name and publicKeyBase64 but an empty/missing instances list when updateServerPeers builds clusters; an instances array that parsed to zero elements (empty array, or all entries filtered out as invalid URLs earlier in parsing).

Common situations: Copy-pasting a cluster block and forgetting the instances array; provisioning template rendering instances = [] because a host list variable was empty; typo in the instances key so it lands in an unknown field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/agent/cluster.go:136

	}
	for _, inst := range sc.instances {
		if inst.hostPort == addr {
			return inst
		}
	}
	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 == "" {

View on GitHub (pinned to 6e04ca5ff0)