hyperledger/fabric · error

attempt to set key %s to version %d, but key is at version %

Error message

attempt to set key %s to version %d, but key is at version %d

What it means

For an existing element, the proposed version must be exactly current+1 (monotonic increment). This error means the update modifies an existing key but supplies the wrong next version — usually a stale version or a skip-ahead version.

Source

Thrown at common/configtx/update.go:88

		return errors.Errorf("delta set was empty -- update would have no effect")
	}

	for key, value := range deltaSet {
		logger.Debugf("Processing change to key: %s", key)
		if err := validateModPolicy(value.modPolicy()); err != nil {
			return errors.Wrapf(err, "invalid mod_policy for element %s", key)
		}

		existing, ok := vi.configMap[key]
		if !ok {
			if value.version() != 0 {
				return errors.Errorf("attempted to set key %s to version %d, but key does not exist", key, value.version())
			}

			continue
		}
		if value.version() != existing.version()+1 {
			return errors.Errorf("attempt to set key %s to version %d, but key is at version %d", key, value.version(), existing.version())
		}

		policy, ok := vi.policyForItem(existing)
		if !ok {
			return errors.Errorf("unexpected missing policy %s for item %s", existing.modPolicy(), key)
		}

		// Ensure the policy is satisfied
		if err := policy.EvaluateSignedData(signedData); err != nil {
			logger.Warnw("policy not satisfied for channel configuration update", "key", key, "policy", policy, "signingIdenties", protoutil.LogMessageForSerializedIdentities(signedData))
			return errors.Wrapf(err, "policy for %s not satisfied", key)
		}
	}
	return nil
}

func verifyFullProposedConfig(writeSet, fullProposedConfig map[string]comparable) error {
	for key := range writeSet {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Fetch the latest config, then set Version to currentVersion + 1 for each modified element.
  2. Rebuild the entire update against a freshly read config block to clear stale versions.
  3. If the error persists, check for concurrent update submissions and serialize admin operations.

Example fix

// before
value.Version = 3 // config element is at version 5
// after
value.Version = existingVersion + 1 // 6
Defensive patterns

Strategy: retry

Validate before calling

if existing, ok := currentConfigMap[key]; ok && value.version() != existing.version()+1 {
  return fmt.Errorf("key %s: expected version %d, got %d", key, existing.version()+1, value.version())
}

Type guard

func nextVersionOK(value comparable, existing comparable) bool { return value.version() == existing.version()+1 }

Try / catch

err := validator.ProposeUpdate(env)
if err != nil && strings.Contains(err.Error(), "but key is at version") {
  env = rebuildUpdateFromLatestConfig() // bump versions and resubmit
}

Prevention

When it happens

Trigger: A delta-set entry whose version() does not equal existing.version()+1, e.g. version 3 when config is at 5, or version 7 when config is at 5.

Common situations: Racing channel updates (another admin bumped the version); a client reusing a write set computed from an older config; manually hard-coded version numbers.

Related errors


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