hyperledger/fabric · error

pending config does not match calculated expected config

Error message

pending config does not match calculated expected config

What it means

During a SmartBFT config update, the orderer re-applies the proposed config envelope through ProposeConfigUpdate to compute what the resulting config SHOULD be, then compares it byte-for-byte (proto.Equal) with the config the proposal actually carried. This error means the two diverged: the config in the submitted CONFIG transaction is not the one the current config would produce when the update is applied. It is thrown from verifyConfigUpdateMsg, reached via ValidateConfig, so the update is rejected and the channel config stays unchanged.

Source

Thrown at orderer/consensus/smartbft/configverifier.go:244

	} else {
		expectedConfigEnv, err = cbv.ConfigUpdateProposer.ProposeConfigUpdate(chdr.ChannelId, confEnv.LastUpdate)
		if err != nil {
			cbv.Logger.Errorf("Rejecting config proposal due to %v", err)
			return err
		}
	}

	if err := cbv.checkConsentersMatchPolicy(confEnv.Config); err != nil {
		return err
	}

	// Extract the Config from the result of ProposeConfigUpdate, and compare it
	// with the pending config.
	if proto.Equal(confEnv.Config, expectedConfigEnv.Config) {
		return nil
	}
	cbv.Logger.Errorf("Pending Config is %v, but it should be %v", confEnv.Config, expectedConfigEnv.Config)
	return errors.Errorf("pending config does not match calculated expected config")
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Regenerate the CONFIG envelope from a fresh, current config block using configtxlator/transport: fetch latest config, apply the ConfigUpdate, recompute, re-sign — do not reuse an old CONFIG envelope
  2. Verify the ConfigUpdate was computed against the latest committed config sequence; refetch the newest config block and retry the update if another update changed the channel in between
  3. Check for concurrent config submitters on the channel and serialize updates
  4. Inspect the logger output 'Pending Config is %v, but it should be %v' to diff the submitted vs expected Config and find the mismatched field

Example fix

// before: reusing a stale CONFIG envelope fetched earlier
oldBlock := fetchConfigBlock(channel)
env := buildConfigEnvelopeFrom(oldBlock) // may mismatch current config
submit(env)

// after: always recompute from the latest committed config
latestBlock := fetchLatestConfigBlock(channel)
update := computeConfigUpdate(latestBlock, desiredChanges)
env, _ := protoutil.CreateSignedEnvelope(..., update, ...)
submit(env)
Defensive patterns

Strategy: validation

Validate before calling

// Recompute the expected config exactly like the orderer does, before submitting
expected, err := endorser.ProposeConfigUpdate(configUpdateEnv, channelID)
if err != nil { return err }
submitted, _ := extractConfig(confEnv)
if !proto.Equal(submitted, expected) {
    return errors.New("config envelope is stale; recompute against latest config block")
}
submit(confEnv)

Prevention

When it happens

Trigger: A CONFIG envelope is proposed whose embedded Config message differs from the recomputed Config produced by ProposeConfigUpdate on the current committed config — e.g. a peer/orderer tampered with or hand-crafted the Config fields instead of deriving them from a ConfigUpdateEnvelope, or the proposal raced with another config change so the expected result shifted.

Common situations: A fabric config update (adding an org, changing batch size, certificate rotation) is submitted against a stale ConfigUpdate so the recomputed result differs; a malicious or buggy client signs a modified Config directly; two config updates race so the second one's expected config no longer matches; tooling that edits channel config blocks manually re-submits an inconsistent Config.

Related errors


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