hyperledger/fabric · error

missing group at path: %s

Error message

missing group at path: %s

What it means

configMapToConfig flattens a cb.ConfigGroup tree into a map keyed by path (e.g. 'groups/Application/groups/MSPs') and recurseConfigMap walks it back into a tree. This error means the walk expected a group entry at the given path but the map had none. It indicates the config map was built incompletely — the comparator (NewComparableConfig) always inserts every group, so this usually implies a corrupted or hand-modified map.

Source

Thrown at common/configtx/configmap.go:116

	}

	return nil
}

// configMapToConfig is intended to be called from outside this file
// It takes a configMap and converts it back into a *cb.ConfigGroup structure
func configMapToConfig(configMap map[string]comparable, rootGroupKey string) (*cb.ConfigGroup, error) {
	rootPath := pathSeparator + rootGroupKey
	return recurseConfigMap(rootPath, configMap)
}

// recurseConfigMap is used only internally by configMapToConfig
// Note, this function no longer mutates the cb.Config* entries within configMap
func recurseConfigMap(path string, configMap map[string]comparable) (*cb.ConfigGroup, error) {
	groupPath := groupPrefix + path
	group, ok := configMap[groupPath]
	if !ok {
		return nil, errors.Errorf("missing group at path: %s", groupPath)
	}

	if group.ConfigGroup == nil {
		return nil, errors.Errorf("ConfigGroup not found at group path: %s", groupPath)
	}

	newConfigGroup := protoutil.NewConfigGroup()
	proto.Merge(newConfigGroup, group.ConfigGroup)

	for key := range group.Groups {
		updatedGroup, err := recurseConfigMap(path+pathSeparator+key, configMap)
		if err != nil {
			return nil, err
		}
		newConfigGroup.Groups[key] = updatedGroup
	}

	for key := range group.Values {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Build the configMap with configtx.NewComparableConfig(group) instead of manually populating it, so every group path is present.
  2. Inspect the reported path in the message and add the missing group entry at that path.
  3. Ensure the root group passed to configMapToConfig is the same one the map was derived from (no mismatched pairing).
  4. Avoid concurrent mutation of the configMap while configMapToConfig runs.

Example fix

// before
configMap := map[string]comparable{}
configMap["groups/Application"] = comparable{ConfigGroup: appGroup}
config := configMapToConfig(configMap) // children missing
// after
configMap := NewComparableConfig(rootGroup) // full tree flattened
config, err := configMapToConfig(configMap)
Defensive patterns

Strategy: validation

Validate before calling

import "strings"

func hasGroupEntry(configMap map[string]configtx.Comparable, path string) bool {
	_, ok := configMap["groups/"+path]
	return ok
}
if !hasGroupEntry(configMap, "Application") {
	return errors.New("configMap lacks group entry for Application")
}

Try / catch

// errors are returned, not panicked
config, err := configtx.ConfigMapToConfig(configMap)
if err != nil {
	return fmt.Errorf("config map conversion failed at %q: %w", reportedPath, err)
}

Prevention

When it happens

Trigger: recurseConfigMap (via configMapToConfig) iterating a group's Groups when the corresponding 'groups/<path>' entry is absent from the configMap — e.g. a configMap built outside NewComparableConfig or mutated concurrently, since recursion also calls itself for child groups.

Common situations: Tests or tools constructing configMap by hand and omitting child group entries; callers passing a different map than the one produced for the same root group; concurrent map modification during conversion.

Related errors


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