{"record":{"id":"7f6a945be69e415a","repo":"OpenNHP/opennhp","slug":"s-servers-d-s-missing-name-clusters-a","errorCode":null,"errorMessage":"%s: [[Servers]][%d] (%s) missing Name — clusters are referenced from resource.toml by Name","messagePattern":"(.+?): \\[\\[Servers\\]\\]\\[(.+?)\\] \\((.+?)\\) missing Name — clusters are referenced from resource\\.toml by Name","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/common/clusterconfig/clusterconfig.go","lineNumber":151,"sourceCode":"\t}\n\tlabel := opts.ConsumerLabel\n\tif label == \"\" {\n\t\tlabel = \"cluster\"\n\t}\n\n\tif len(clusters) == 0 {\n\t\treturn fmt.Errorf(\"%s: no [[Servers]] configured\", label)\n\t}\n\tfor i, c := range clusters {\n\t\tif c == nil {\n\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] is nil\", label, i)\n\t\t}\n\t\tif c.PubKeyBase64 == \"\" {\n\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] missing PubKeyBase64\", label, i)\n\t\t}\n\t\tif opts.RequireName {\n\t\t\tif c.Name == \"\" {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] (%s) missing Name — clusters are referenced from resource.toml by Name\",\n\t\t\t\t\tlabel, i, c.PubKeyBase64)\n\t\t\t}\n\t\t\tif len(c.Name) > NameMaxLen {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] Name %q exceeds %d chars\",\n\t\t\t\t\tlabel, i, c.Name, NameMaxLen)\n\t\t\t}\n\t\t\tif !clusterNameRegex.MatchString(c.Name) {\n\t\t\t\treturn fmt.Errorf(\"%s: [[Servers]][%d] Name %q invalid — allowed chars: [a-zA-Z0-9._-]\",\n\t\t\t\t\tlabel, i, c.Name)\n\t\t\t}\n\t\t}\n\n\t\tlegacy := c.hasLegacyFields()\n\t\thasInstances := len(c.Instances) > 0\n\n\t\tswitch {\n\t\tcase legacy && hasInstances:\n\t\t\t// Both forms in one entry is almost certainly an","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/common/clusterconfig/clusterconfig.go#L133-L169","documentation":"When opts.RequireName is set, Normalize additionally demands a non-empty Name per cluster, because clusters are referenced from resource.toml by Name rather than by key or index. An anonymous cluster cannot be referenced and is rejected, with the entry's key echoed for identification.","triggerScenarios":"Calling Normalize with RequireName=true (e.g. from normalizeClusters for multi-cluster deployments) and clusters[i].Name == \"\".","commonSituations":"Upgrading from a single-cluster config where Name was optional to the multi-cluster format that requires it; docker-compose demo configs copied without adding Name; resource.toml referencing clusters by Name while server.toml entries lack the field.","solutions":["Add a unique Name to every [[Servers]] block in the cluster config","Use the same Name string in resource.toml cluster references so lookups resolve","Keep each Name within NameMaxLen — check the surrounding validation for the length limit","Update config templates so future deployments always emit Name alongside PubKeyBase64"],"exampleFix":"// before (server.toml)\n[[Servers]]\nPubKeyBase64 = \"<key>\"\n// after\n[[Servers]]\nName = \"nhp-server-1\"\nPubKeyBase64 = \"<key>\"","handlingStrategy":"validation","validationCode":"for i, c := range clusters {\n    if c.Name == \"\" {\n        return fmt.Errorf(\"cluster %d needs a Name (referenced from resource.toml)\", i)\n    }\n}\nerr := clusterconfig.Normalize(clusters, clusterconfig.NormalizeOptions{RequireName: true, ...})","typeGuard":null,"tryCatchPattern":"if err := clusterconfig.Normalize(clusters, opts); err != nil {\n    if strings.Contains(err.Error(), \"missing Name\") {\n        return fmt.Errorf(\"multi-cluster configs require per-cluster Name fields: %w\", err)\n    }\n    return err\n}","preventionTips":["Mandate Name in every [[Servers]] block even for single-cluster setups","Keep a mapping check in CI: every cluster Name referenced by resource.toml exists in server.toml","Update config templates when migrating from the single-cluster demo format"],"tags":["config","toml","naming"],"backgroundTag":"missing-required-config-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"}