{"record":{"id":"1f4095550d6599c4","repo":"hyperledger/fabric","slug":"field-config-channelgroup-groups-is-nil","errorCode":null,"errorMessage":"field Config.ChannelGroup.Groups is nil","messagePattern":"field Config\\.ChannelGroup\\.Groups is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"discovery/support/config/support.go","lineNumber":234,"sourceCode":"\t\t\t\treturn errors.Wrap(err, \"failed marshaling FabricMSPConfig\")\n\t\t\t}\n\t\t\tif _, exists := output[fabricConfig.Name]; exists {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toutput[fabricConfig.Name] = fabricConfig\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc ValidateConfig(c *common.Config) error {\n\tif c.ChannelGroup == nil {\n\t\treturn errors.New(\"field Config.ChannelGroup is nil\")\n\t}\n\tgrps := c.ChannelGroup.Groups\n\tif grps == nil {\n\t\treturn errors.New(\"field Config.ChannelGroup.Groups is nil\")\n\t}\n\tfor _, field := range []string{channelconfig.OrdererGroupKey, channelconfig.ApplicationGroupKey} {\n\t\tgrp, exists := grps[field]\n\t\tif !exists {\n\t\t\treturn fmt.Errorf(\"key Config.ChannelGroup.Groups[%s] is missing\", field)\n\t\t}\n\t\tif grp.Groups == nil {\n\t\t\treturn fmt.Errorf(\"key Config.ChannelGroup.Groups[%s].Groups is nil\", field)\n\t\t}\n\t}\n\tif c.ChannelGroup.Values == nil {\n\t\treturn errors.New(\"field Config.ChannelGroup.Values is nil\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":216,"sourceCodeEnd":250,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/discovery/support/config/support.go#L216-L250","documentation":"ValidateConfig requires Config.ChannelGroup.Groups to be non-nil because Config later indexes it for the Orderer and Application groups. A nil Groups map means the channel group exists but carries no sub-groups at all, which is structurally invalid for a Fabric channel config, so the discovery Config request is rejected with 'config is invalid'.","triggerScenarios":"A common.Config whose ChannelGroup is set but has Groups == nil: (1) fixtures constructed as &common.Config{ChannelGroup: &common.ConfigGroup{}}; (2) unmarshaling a protobuf that only had Values populated; (3) a config cache entry overwritten with a partially populated struct by a custom getter.","commonSituations":"Unit-test fixtures missing the Groups map; hand-rolled mocks of CurrentConfigGetter; truncated config unmarshals from a corrupted config block; tools that copied only part of the channel config.","solutions":["Populate ChannelGroup.Groups with at least the \"Orderer\" and \"Application\" keys, each having its own Groups map.","Verify the source config block was fully unmarshaled from envelope payload data of type common.Config.","Run ValidateConfig on the fetched config before caching/serving it, and re-fetch the latest config from the orderer on failure.","Fix test/mocks to mirror the real channel structure produced by configtxgen."],"exampleFix":"// before\nChannelGroup: &common.ConfigGroup{Values: map[string]*common.ConfigValue{}}\n\n// after\nChannelGroup: &common.ConfigGroup{\n  Groups: map[string]*common.ConfigGroup{\n    channelconfig.OrdererGroupKey:     {Groups: map[string]*common.ConfigGroup{}},\n    channelconfig.ApplicationGroupKey: {Groups: map[string]*common.ConfigGroup{}},\n  },\n  Values: map[string]*common.ConfigValue{},\n}","handlingStrategy":"validation","validationCode":"if err := config.ValidateConfig(cfg); err != nil {\n\treturn err // covers nil/missing Groups before Config() is called\n}\nif _, ok := cfg.ChannelGroup.Groups[channelconfig.OrdererGroupKey]; !ok {\n\treturn errors.New(\"Orderer group missing\")\n}\nif _, ok := cfg.ChannelGroup.Groups[channelconfig.ApplicationGroupKey]; !ok {\n\treturn errors.New(\"Application group missing\")\n}","typeGuard":"func hasSubGroups(c *common.Config) bool {\n\treturn c != nil && c.ChannelGroup != nil && c.ChannelGroup.Groups != nil &&\n\t\tlen(c.ChannelGroup.Groups) > 0\n}","tryCatchPattern":"res, err := support.Config(channel)\nif err != nil && strings.Contains(err.Error(), \"Config.ChannelGroup.Groups is nil\") {\n\tlogger.Warn(\"channel config has no sub-groups; refreshing config from orderer\")\n\treturn refreshConfigAndRetry(channel)\n}","preventionTips":["Model test fixtures on real configtxgen output so Groups is always populated.","Validate configs with discovery config.ValidateConfig immediately after unmarshaling, before caching.","Treat any config whose ChannelGroup.Groups is empty as corrupt and re-fetch from the ledger.","Avoid hand-copying config structs between services; serialize with protobuf end-to-end."],"tags":["hyperledger-fabric","service-discovery","config-validation","nil-map"],"backgroundTag":"missing-config-field","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}