{"record":{"id":"f63379d37477de56","repo":"hyperledger/fabric","slug":"field-config-channelgroup-is-nil","errorCode":null,"errorMessage":"field Config.ChannelGroup is nil","messagePattern":"field Config\\.ChannelGroup is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"discovery/support/config/support.go","lineNumber":230,"sourceCode":"\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfabricConfig := &msp.FabricMSPConfig{}\n\t\t\tif err := proto.Unmarshal(mspConfig.Config, fabricConfig); err != nil {\n\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","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/discovery/support/config/support.go#L212-L248","documentation":"ValidateConfig sanity-checks a common.Config retrieved by CurrentConfigGetter.GetCurrConfig before DiscoverySupport.Config uses it. This error means the Config struct has a nil ChannelGroup, i.e. the top-level channel configuration group is absent, so no channel structure exists to walk. The caller (Config) wraps it with 'config is invalid'.","triggerScenarios":"GetCurrConfig(channel) returns a non-nil *common.Config that was constructed without a ChannelGroup: (1) a config block unmarshaled into the wrong message type; (2) a hand-built or default (zero-value) common.Config stored in the config cache; (3) an empty/partially unmarshaled protobuf where the ChannelGroup field was never populated.","commonSituations":"Custom CurrentConfigGetterFunc implementations returning &common.Config{} placeholders in tests or mocks; config caches populated from an empty/malformed envelope; migration tooling that wrote an empty Config struct; unit tests exercising discovery support with incomplete fixtures.","solutions":["Fix the CurrentConfigGetter implementation so it returns a fully populated common.Config whose ChannelGroup contains the Orderer/Application groups and Values.","If the config comes from a block, unmarshal the envelope's payload data into common.Config (not common.Block or another type) and verify err == nil.","Call config.ValidateConfig(cfg) defensively before serving discovery queries and fall back to re-fetching the latest config block when validation fails.","In tests, build fixtures with all required fields: ChannelGroup.Groups{\"Orderer\": ..., \"Application\": ...} and non-nil Values."],"exampleFix":"// before\ncfg := &common.Config{} // ChannelGroup nil\n\n// after\ncfg := &common.Config{\n  ChannelGroup: &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  },\n}","handlingStrategy":"validation","validationCode":"cfg := getter.GetCurrConfig(channel)\nif cfg == nil || cfg.ChannelGroup == nil {\n\treturn fmt.Errorf(\"channel %s config is missing ChannelGroup; re-fetch latest config block\", channel)\n}\nif err := config.ValidateConfig(cfg); err != nil {\n\treturn fmt.Errorf(\"config is invalid: %w\", err)\n}","typeGuard":"func hasChannelGroup(c *common.Config) bool {\n\treturn c != nil && c.ChannelGroup != nil\n}","tryCatchPattern":"res, err := support.Config(channel)\nif err != nil && strings.Contains(err.Error(), \"field Config.ChannelGroup is nil\") {\n\t// rebuild/refresh the config cache entry from the newest config block before retrying\n\terr = refreshAndRetry(channel)\n}","preventionTips":["Never return a zero-value &common.Config{} from custom CurrentConfigGetter implementations; return nil so callers distinguish 'no config' from 'bad config'.","Run discovery config.ValidateConfig on every config before caching it.","Unmarshal config envelopes' payload data strictly into common.Config and check errors.","In tests, construct fixtures with a fully populated ChannelGroup."],"tags":["hyperledger-fabric","service-discovery","config-validation","nil-field"],"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"}