hyperledger/fabric · error

writeset contained key %s which did not appear in proposed c

Error message

writeset contained key %s which did not appear in proposed config

What it means

verifyFullProposedConfig found a key present in the update's writeSet that is absent from the fully computed proposed config. The delta application (computeUpdateResult) should produce a config containing every written key; if one is missing, the writeSet references a key that mapConfig/the update machinery cannot place in the resulting config, indicating a malformed or inconsistent update.

Source

Thrown at common/configtx/update.go:108

		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 {
		if _, ok := fullProposedConfig[key]; !ok {
			return errors.Errorf("writeset contained key %s which did not appear in proposed config", key)
		}
	}
	return nil
}

// authorizeUpdate validates that all modified config has the corresponding modification policies satisfied by the signature set
// it returns a map of the modified config
func (vi *ValidatorImpl) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
	if configUpdateEnv == nil {
		return nil, errors.Errorf("cannot process nil ConfigUpdateEnvelope")
	}

	configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
	if err != nil {
		return nil, err
	}

	if configUpdate.ChannelId != vi.channelID {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Regenerate the update from the actual current config using configtxlator (fetch config, decode, diff, encode) instead of hand-crafting the writeSet.
  2. Compare the writeSet keys against the current channel config tree to find the orphan key.
  3. Verify the update targets the same channel and was derived from that channel's latest config block.
  4. Remove the stray key from the writeSet if it is not a legitimate change.

Example fix

// before: hand-built writeSet with key "Groups/Orderer/Org1/OldKey" absent in proposed tree
// after: diff current vs desired config via configtxlator so every writeSet key
// appears in the recomputed full config
config, _ := fetchAndDecodeChannelConfig(channelID)
update := computeDiff(config, desiredConfig)
Defensive patterns

Strategy: validation

Validate before calling

// Every writeSet key must be resolvable in the config tree before proposing
func writeSetKeysResolvable(update *cb.ConfigUpdate) error {
    for key := range update.GetWriteSet().GetValues() {
        if key == "" { return fmt.Errorf("empty value key") }
    }
    for g := range update.GetWriteSet().GetGroups() {
        if g == "" { return fmt.Errorf("empty group key") }
    }
    return nil
}

Try / catch

if _, err := validator.ProposeConfigUpdate(env, seq); err != nil {
    if strings.Contains(err.Error(), "did not appear in proposed config") {
        // rebuild update via configtxlator diff from current config
    }
    return err
}

Prevention

When it happens

Trigger: Calling proposeConfigUpdate/Validate with a writeSet key that computeUpdateResult dropped — e.g. a key whose namespace/group mapping fails, or a delta applied to a path that does not exist in the full proposed config.

Common situations: Constructing a ConfigUpdate programmatically with keys not matching the config tree structure; using an update file generated against a different channel or config version; corrupted/partially serialized write-set after manual proto editing.

Related errors


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