{"record":{"id":"c61c2ad1255f512c","repo":"OpenNHP/opennhp","slug":"cluster-q-s-no-instances-configured","errorCode":null,"errorMessage":"cluster %q (%s): no instances configured","messagePattern":"cluster %q \\((.+?)\\): no instances configured","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/agent/cluster.go","lineNumber":136,"sourceCode":"\t}\n\tfor _, inst := range sc.instances {\n\t\tif inst.hostPort == addr {\n\t\t\treturn inst\n\t\t}\n\t}\n\treturn nil\n}\n\n// buildCluster turns a parsed ClusterConfig into a runtime cluster.\n// The returned cluster's representativePeer is NOT yet registered on a\n// device — callers (updateServerPeers) are responsible for that, so\n// they can also handle peer removal on reload.\nfunc buildCluster(cfg *ClusterConfig) (*ServerCluster, error) {\n\tif cfg.PubKeyBase64 == \"\" {\n\t\treturn nil, fmt.Errorf(\"cluster %q: missing publicKeyBase64\", cfg.Name)\n\t}\n\tif len(cfg.Instances) == 0 {\n\t\treturn nil, fmt.Errorf(\"cluster %q (%s): no instances configured\",\n\t\t\tcfg.Name, cfg.PubKeyBase64)\n\t}\n\tif err := cfg.LoadBalance.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"cluster %q (%s): %w\",\n\t\t\tcfg.Name, cfg.PubKeyBase64, err)\n\t}\n\n\tsc := &ServerCluster{\n\t\tPublicKeyBase64: cfg.PubKeyBase64,\n\t\tName:            cfg.Name,\n\t\tSticky:          cfg.StickyOrDefault(),\n\t\tinstances:       make([]*ServerInstance, 0, len(cfg.Instances)),\n\t}\n\n\tfor i, ic := range cfg.Instances {\n\t\thost := ic.Host\n\t\tip := ic.Ip\n\t\tif host == \"\" && ip == \"\" {","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/agent/cluster.go#L118-L154","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Add at least one reachable server instance URL (udp://host:port) to the cluster's instances list","Check the rendered config for an empty instances value caused by an unset template variable","Verify the instances field name spelling matches the parser so entries are not silently dropped","Fail fast in config loading with the cluster name, which this error already provides — fix the offending cluster block"],"exampleFix":"// before\n[[clusters]]\nname = \"nhp-server\"\npublicKeyBase64 = \"abc...\"\n// after\n[[clusters]]\nname = \"nhp-server\"\npublicKeyBase64 = \"abc...\"\ninstances = [\"udp://nhp1.example.com:5555\", \"udp://nhp2.example.com:5555\"]","handlingStrategy":"validation","validationCode":"if len(cfg.Instances) == 0 {\n\treturn fmt.Errorf(\"cluster %q: at least one instance URL required\", cfg.Name)\n}\nfor _, u := range cfg.Instances {\n\tif _, err := url.Parse(u); err != nil { return err }\n}","typeGuard":null,"tryCatchPattern":"cl, err := buildCluster(cfg)\nif err != nil {\n\treturn fmt.Errorf(\"cluster %s rejected: %w\", cfg.Name, err)\n}","preventionTips":["Never template an empty instances list; fail the render if the host list is empty","Validate instance URL syntax at parse time","Lint agent config files in CI before deployment"],"tags":["config","agent","cluster","validation"],"backgroundTag":"empty-required-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"}