hyperledger/fabric · error
proposed update requires that key %s be at version %d, but i
Error message
proposed update requires that key %s be at version %d, but it is currently at version %d
What it means
verifyReadSet also checks version equality: each read-set entry must match the current version of that config element. If the element has been modified since the update was prepared, the read set is stale and the update is rejected with this error.
Source
Thrown at common/configtx/update.go:27
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
}
// If the key in the readset is a different version, we include it
// Error checking on the sanity of the update is done against the config
result[key] = value
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Fetch the newest config block, recompute read/write sets at the current versions, and resubmit.
- Automate config fetch -> update -> submit atomically to avoid racing other updates.
- Identify the concurrent update that bumped the version and merge your changes on top of it.
Example fix
// before
readSet[key] = comparable{ConfigValue: &cb.ConfigValue{Version: 3}} // config now at 4
// after
current := vi.configMap[key]; readSet[key] = comparable{ConfigValue: &cb.ConfigValue{Version: current.version(), Value: newVal}} Defensive patterns
Strategy: retry
Validate before calling
for key, value := range update.GetReadSet() {
if current[key].Version() != value.Version() {
return fmt.Errorf("version drift on %s: read %d, current %d", key, value.Version(), current[key].Version())
}
} Type guard
func versionsMatch(value comparable, existing comparable) bool { return value.version() == existing.version() } Try / catch
err := validator.ProposeUpdate(env)
if err != nil && strings.Contains(err.Error(), "currently at version") {
env = rebuildUpdateFromLatestConfig() // fetch fresh config, rebuild, resubmit
} Prevention
- Minimize the window between reading config and submitting the update
- Detect concurrent updates and merge before resubmitting
- Never hard-code version numbers in generated updates
When it happens
Trigger: Submitting a channel update whose read_set entry has version N while the current config element is at a version > N (it was changed by a prior update).
Common situations: Another admin updated channel config between the client fetching the config and submitting its update; optimistic-concurrency failure on channel configuration.
Related errors
- attempted to set key %s to version %d, but key does not exis
- attempt to set key %s to version %d, but key is at version %
- error validating ReadSet
- Attempted to set the batch size max message count to an inva
- Attempted to set the batch size absolute max bytes to an inv
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/e29a9414ccab1002.
Report an issue: GitHub.