hyperledger/fabric · error

existing config does not contain element for %s but was in t

Error message

existing config does not contain element for %s but was in the read set

What it means

verifyReadSet validates that every element in the proposed update's read set exists in the current committed config map. This error means the update references a config key that no longer exists in the current configuration, so the update is rejected.

Source

Thrown at common/configtx/update.go:23

*/

package configtx

import (
	"maps"
	"strings"

	cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
	"github.com/hyperledger/fabric/common/policies"
	"github.com/hyperledger/fabric/protoutil"
	"github.com/pkg/errors"
)

func (vi *ValidatorImpl) verifyReadSet(readSet map[string]comparable) error {
	for key, value := range readSet {
		existing, ok := vi.configMap[key]
		if !ok {
			return errors.Errorf("existing config does not contain element for %s but was in the read set", key)
		}

		if existing.version() != value.version() {
			return errors.Errorf("proposed update requires that key %s be at version %d, but it is currently at version %d", key, value.version(), existing.version())
		}
	}
	return nil
}

func computeDeltaSet(readSet, writeSet map[string]comparable) map[string]comparable {
	result := make(map[string]comparable)
	for key, value := range writeSet {
		readVal, ok := readSet[key]

		if ok && readVal.version() == value.version() {
			continue
		}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Re-fetch the latest channel config block and rebuild the update envelope.
  2. Remove stale keys from the read set of the ConfigUpdate.
  3. If a key was intentionally removed, submit an update that reflects the deletion in the write set instead of the read set.

Example fix

// before
readSet["/Channel/Application/Org3"] = org3Comparable // Org3 was removed
// after
config := fetchLatestConfig(); readSet := buildReadSet(config) // rebuild from current config
Defensive patterns

Strategy: retry

Validate before calling

for key := range update.GetReadSet() {
  if _, ok := currentConfigMap[key]; !ok {
    return fmt.Errorf("stale read set: %s not in current config", key)
  }
}

Type guard

func readSetIsCurrent(readSet map[string]*cb.ConfigGroup, current map[string]comparable) bool { for k := range readSet { if _, ok := current[k]; !ok { return false } }; return true }

Try / catch

err := validator.ProposeUpdate(env)
if err != nil && strings.Contains(err.Error(), "but was in the read set") {
  latest := fetchLatestConfigBlock()
  env = rebuildUpdate(latest) // rebuild and retry once
}

Prevention

When it happens

Trigger: Calling channel update where the ConfigUpdate's read_set contains a key deleted from the channel config since the update envelope was prepared.

Common situations: Two admins prepare concurrent updates; one removes an org/group and the other's stale update still reads it; client built the envelope against an outdated config block.

Related errors


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