hyperledger/fabric · error

ConfigEnvelope LastUpdate did not produce the supplied confi

Error message

ConfigEnvelope LastUpdate did not produce the supplied config result

What it means

After converting LastUpdate back to a channelGroup, Validate checks that the reconstructed group exactly equals the ChannelGroup supplied in the ConfigEnvelope (using proto.Equal). If they differ, the envelope is internally inconsistent — the claimed final config doesn't match what the update produces.

Source

Thrown at common/configtx/validator.go:192

	configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configEnv.LastUpdate)
	if err != nil {
		return err
	}

	configMap, err := vi.authorizeUpdate(configUpdateEnv)
	if err != nil {
		return err
	}

	channelGroup, err := configMapToConfig(configMap, vi.namespace)
	if err != nil {
		return errors.Errorf("could not turn configMap back to channelGroup: %s", err)
	}

	// reflect.Equal will not work here, because it considers nil and empty maps as different
	if !proto.Equal(channelGroup, configEnv.Config.ChannelGroup) {
		return errors.Errorf("ConfigEnvelope LastUpdate did not produce the supplied config result")
	}

	return nil
}

// ChannelID retrieves the channel ID associated with this manager
func (vi *ValidatorImpl) ChannelID() string {
	return vi.channelID
}

// Sequence returns the sequence number of the config
func (vi *ValidatorImpl) Sequence() uint64 {
	return vi.sequence
}

// ConfigProto returns the config proto which initialized this Validator
func (vi *ValidatorImpl) ConfigProto() *cb.Config {
	return vi.configProto

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Recompute LastUpdate (computeDeltaConfigUpdate) from the current config and the desired final config so both parts agree
  2. Regenerate the whole envelope with configtxlator's 'update' output instead of hand-editing ChannelGroup
  3. Compare ChannelGroup and the reconstructed group to find the differing field
  4. Use configtxgen to build the update from the same source profile used for the config

Example fix

// before
env.Config.ChannelGroup = editedGroup // LastUpdate stale
// after
update := computeDeltaConfigUpdate(currentConfig, editedGroup)
env.LastUpdate = wrapConfigUpdate(update)
env.Config.ChannelGroup = editedGroup
Defensive patterns

Strategy: validation

Validate before calling

// ensure update result matches the claimed final config before submit
recomputed, err := configMapToConfig(configMap, namespace)
if err != nil || !proto.Equal(recomputed, env.Config.ChannelGroup) {
    return errors.New("recompute LastUpdate from current+desired config")
}

Try / catch

if err := validator.Validate(env, fullPayload); err != nil {
    if strings.Contains(err.Error(), "did not produce the supplied config result") {
        env = recomputeEnvelope(currentConfig, desiredConfig) // rebuild both parts together
    }
}

Prevention

When it happens

Trigger: Submitting a ConfigEnvelope where Config.ChannelGroup was edited independently of LastUpdate — e.g. manually patching the full config but leaving a stale/different update transaction inside.

Common situations: Manually editing config via configtxlator then forgetting to recompute LastUpdate; tools that generate the full config but copy an old update blob; merging updates from different sources.

Related errors


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