hyperledger/fabric · error

attempted to set key %s to version %d, but key does not exis

Error message

attempted to set key %s to version %d, but key does not exist

What it means

For a new element (absent from the current config map), the proposed version must be 0. This error means the update tries to add a key that doesn't currently exist but assigns it a non-zero version, which is inconsistent with how the versioning works.

Source

Thrown at common/configtx/update.go:82

	}
	return nil
}

func (vi *ValidatorImpl) verifyDeltaSet(deltaSet map[string]comparable, signedData []*protoutil.SignedData) error {
	if len(deltaSet) == 0 {
		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)
		}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set the new element's Version to 0 when adding a key that does not exist in current config.
  2. Rebuild the write set from the current config rather than cached comparisons.
  3. If the element should exist, first check whether a concurrent update deleted it and reconcile.

Example fix

// before
delta["/Channel/Application/Org4"] = comparable{ConfigGroup: &cb.ConfigGroup{Version: 2}} // key absent
// after
delta["/Channel/Application/Org4"] = comparable{ConfigGroup: &cb.ConfigGroup{Version: 0}}
Defensive patterns

Strategy: validation

Validate before calling

if _, ok := currentConfigMap[key]; !ok && value.version() != 0 {
  return fmt.Errorf("new key %s must have version 0", key)
}

Type guard

func newElementVersionOK(key string, value comparable, current map[string]comparable) bool {
  _, exists := current[key]
  return exists || value.version() == 0
}

Try / catch

err := validator.ProposeUpdate(env)
if err != nil && strings.Contains(err.Error(), "but key does not exist") {
  resetVersionsToZeroForNewKeys(deltaSet, currentConfigMap)
  env = rebuildEnvelope(deltaSet) // rebuild and retry once
}

Prevention

When it happens

Trigger: A delta-set entry for a key not in configMap with version() != 0 — e.g. reusing a cached comparable from an old config where the element previously existed but has since been deleted.

Common situations: Re-adding a removed org/group while carrying the old version number; copying a delta set from a different channel config.

Related errors


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