hyperledger/fabric · error

field Config.ChannelGroup.Groups is nil

Error message

field Config.ChannelGroup.Groups is nil

What it means

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'.

Source

Thrown at discovery/support/config/support.go:234

				return errors.Wrap(err, "failed marshaling FabricMSPConfig")
			}
			if _, exists := output[fabricConfig.Name]; exists {
				continue
			}
			output[fabricConfig.Name] = fabricConfig
		}
	}

	return nil
}

func ValidateConfig(c *common.Config) error {
	if c.ChannelGroup == nil {
		return errors.New("field Config.ChannelGroup is nil")
	}
	grps := c.ChannelGroup.Groups
	if grps == nil {
		return errors.New("field Config.ChannelGroup.Groups is nil")
	}
	for _, field := range []string{channelconfig.OrdererGroupKey, channelconfig.ApplicationGroupKey} {
		grp, exists := grps[field]
		if !exists {
			return fmt.Errorf("key Config.ChannelGroup.Groups[%s] is missing", field)
		}
		if grp.Groups == nil {
			return fmt.Errorf("key Config.ChannelGroup.Groups[%s].Groups is nil", field)
		}
	}
	if c.ChannelGroup.Values == nil {
		return errors.New("field Config.ChannelGroup.Values is nil")
	}
	return nil
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Populate ChannelGroup.Groups with at least the "Orderer" and "Application" keys, each having its own Groups map.
  2. Verify the source config block was fully unmarshaled from envelope payload data of type common.Config.
  3. Run ValidateConfig on the fetched config before caching/serving it, and re-fetch the latest config from the orderer on failure.
  4. Fix test/mocks to mirror the real channel structure produced by configtxgen.

Example fix

// before
ChannelGroup: &common.ConfigGroup{Values: map[string]*common.ConfigValue{}}

// after
ChannelGroup: &common.ConfigGroup{
  Groups: map[string]*common.ConfigGroup{
    channelconfig.OrdererGroupKey:     {Groups: map[string]*common.ConfigGroup{}},
    channelconfig.ApplicationGroupKey: {Groups: map[string]*common.ConfigGroup{}},
  },
  Values: map[string]*common.ConfigValue{},
}
Defensive patterns

Strategy: validation

Validate before calling

if err := config.ValidateConfig(cfg); err != nil {
	return err // covers nil/missing Groups before Config() is called
}
if _, ok := cfg.ChannelGroup.Groups[channelconfig.OrdererGroupKey]; !ok {
	return errors.New("Orderer group missing")
}
if _, ok := cfg.ChannelGroup.Groups[channelconfig.ApplicationGroupKey]; !ok {
	return errors.New("Application group missing")
}

Type guard

func hasSubGroups(c *common.Config) bool {
	return c != nil && c.ChannelGroup != nil && c.ChannelGroup.Groups != nil &&
		len(c.ChannelGroup.Groups) > 0
}

Try / catch

res, err := support.Config(channel)
if err != nil && strings.Contains(err.Error(), "Config.ChannelGroup.Groups is nil") {
	logger.Warn("channel config has no sub-groups; refreshing config from orderer")
	return refreshConfigAndRetry(channel)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/1f4095550d6599c4. Report an issue: GitHub.