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
- Always set LastUpdate to the signed ConfigUpdateEnvelope when constructing a cb.ConfigEnvelope
- Use the standard flow: create the update with configtxlator, wrap it, sign, and submit rather than assembling envelopes by hand
- 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
- Populate both Config and LastUpdate when constructing ConfigEnvelope
- Use configtxlator/configtxgen tooling rather than hand-built protos
- Unit-test envelope construction before submitting to the network
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
- config transaction inspection failed
- failed to parse config
- next config attempted to change ConsensusType.State to %s, b
- attempted to change ConsensusType.Type from %s to %s, but Co
- attempted to change ConsensusType.Metadata, but ConsensusTyp
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/827c2998931f42d3.
Report an issue: GitHub.