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
- Re-fetch the latest channel config block and rebuild the update envelope.
- Remove stale keys from the read set of the ConfigUpdate.
- 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
- Always derive read/write sets from the most recent config block
- Serialize admin channel-update operations
- Retry the whole fetch->update->submit cycle on this error
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
- delta set was empty -- update would have no effect
- Attempted to set the batch size max message count to an inva
- Attempted to set the batch size absolute max bytes to an inv
- Attempted to set the batch size preferred max bytes to an in
- Attempted to set the batch size preferred max bytes (%v) gre
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/2638593cb9fb8f75.
Report an issue: GitHub.