{"record":{"id":"19d8c1c2992750a0","repo":"OpenNHP/opennhp","slug":"cluster-q-s-w","errorCode":null,"errorMessage":"cluster %q (%s): %w","messagePattern":"cluster %q \\((.+?)\\): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/agent/cluster.go","lineNumber":140,"sourceCode":"\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 == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"cluster %q instance #%d: must set either Host or Ip\",\n\t\t\t\tcfg.Name, i)\n\t\t}\n\t\tif ic.Port <= 0 {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/agent/cluster.go#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set loadBalance to one of the exact supported values (check the LoadBalance type/Validate in endpoints/agent)","Check the wrapped error text for the offending value and correct the casing/spelling","Omit the field to use the default scheme if one exists","Add the accepted values to config documentation/templates to prevent recurrence"],"exampleFix":"// before\n[[clusters]]\nname = \"nhp-server\"\npublicKeyBase64 = \"abc...\"\nloadBalance = \"least-connections\"   # unsupported\n// after\n[[clusters]]\nname = \"nhp-server\"\npublicKeyBase64 = \"abc...\"\nloadBalance = \"roundRobin\"          # supported scheme","handlingStrategy":"validation","validationCode":"var lb LoadBalance\nif err := lb.UnmarshalText([]byte(cfg.LoadBalance)); err != nil {\n\treturn fmt.Errorf(\"cluster %q: bad loadBalance %q (allowed: %v)\", cfg.Name, cfg.LoadBalance, lb.Allowed())\n}","typeGuard":null,"tryCatchPattern":"cl, err := buildCluster(cfg)\nif err != nil {\n\tvar le *LoadBalanceError\n\tif errors.As(err, &le) { /* fix the scheme value */ }\n}","preventionTips":["Use a typed enum with strict unmarshalling for load balance settings","Document exact accepted values in config templates/examples","Reject unknown values at config parse time, not cluster build time"],"tags":["config","agent","cluster","load-balance"],"backgroundTag":"invalid-enum-value","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"}