hyperledger/fabric · error

updated config does not include a config update

Error message

updated config does not include a config update

What it means

MaintenanceFilter.inspect returns this when configEnvelope.LastUpdate is nil. The ConfigEnvelope carries the resulting Config plus the LastUpdate (the signed config-update envelope that produced it); a missing LastUpdate means the filter cannot verify that the state transition carries only the ConsensusType change. This typically indicates a hand-crafted or incorrectly constructed ConfigEnvelope.

Source

Thrown at orderer/common/msgprocessor/maintenancefilter.go:81

	if err != nil {
		return errors.Wrap(err, "envelope unmarshalling failed")
	}

	logger.Debugw("Going to inspect maintenance mode transition rules",
		"ConsensusState", ordererConf.ConsensusState(), "channel", chanHdr.ChannelId)
	err = mf.inspect(configEnvelope, ordererConf)
	if err != nil {
		return errors.Wrap(err, "config transaction inspection failed")
	}

	return nil
}

// inspect checks whether the next orderer config, extracted from the incoming configEnvelope, respects the
// transition rules of consensus-type migration using maintenance-mode.
func (mf *MaintenanceFilter) inspect(configEnvelope *cb.ConfigEnvelope, ordererConfig channelconfig.Orderer) error {
	if configEnvelope.LastUpdate == nil {
		return errors.Errorf("updated config does not include a config update")
	}

	bundle, err := channelconfig.NewBundle(mf.support.ChannelID(), configEnvelope.Config, mf.bccsp)
	if err != nil {
		return errors.Wrap(err, "failed to parse config")
	}

	nextOrdererConfig, ok := bundle.OrdererConfig()
	if !ok {
		return errors.New("next config is missing orderer group")
	}

	if !ordererConfig.Capabilities().ConsensusTypeMigration() {
		if nextState := nextOrdererConfig.ConsensusState(); nextState != orderer.ConsensusType_STATE_NORMAL {
			return errors.Errorf("next config attempted to change ConsensusType.State to %s, but capability is disabled", nextState)
		}
		if ordererConfig.ConsensusType() != nextOrdererConfig.ConsensusType() {
			return errors.Errorf("next config attempted to change ConsensusType.Type from %s to %s, but capability is disabled",

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Always set LastUpdate to the signed ConfigUpdateEnvelope when constructing a cb.ConfigEnvelope
  2. Use the standard flow: create the update with configtxlator, wrap it, sign, and submit rather than assembling envelopes by hand
  3. Check protoutil.CreateSignedEnvelope usage includes the ConfigEnvelope with both fields populated

Example fix

// before
env := &cb.ConfigEnvelope{Config: nextConfig}
// after
env := &cb.ConfigEnvelope{Config: nextConfig, LastUpdate: signedConfigUpdateEnv}
Defensive patterns

Strategy: validation

Validate before calling

if configEnvelope.LastUpdate == nil {
	return errors.New("ConfigEnvelope.LastUpdate must reference the signed config-update envelope")
}

Prevention

When it happens

Trigger: Submitting a cb.ConfigEnvelope with Config set but LastUpdate nil to the ordering service as a CONFIG transaction (e.g. building the envelope manually instead of via the configtxgen/configtxlator flow).

Common situations: Custom tooling that assembles ConfigEnvelope structs directly; tests that populate only Config; a serialization path that drops LastUpdate.

Related errors


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