hyperledger/fabric · error

bad config message: %s

Error message

bad config message: %s

What it means

Returned when a config-type message fails revalidation via support.ProcessConfigMsg after the config sequence has advanced since it was signed. The message was classified as a config update, but its content cannot be processed into a valid config envelope, so it is rejected as pending.

Source

Thrown at orderer/consensus/etcdraft/chain.go:937

//	-- err error; the error encountered, if any.
//
// It takes care of config messages as well as the revalidation of messages if the config sequence has advanced.
func (c *Chain) ordered(msg *orderer.SubmitRequest) (batches [][]*common.Envelope, pending bool, err error) {
	seq := c.support.Sequence()

	isconfig, err := c.isConfig(msg.GetPayload())
	if err != nil {
		return nil, false, errors.Errorf("bad message: %s", err)
	}

	if isconfig {
		// ConfigMsg
		if msg.GetLastValidationSeq() < seq {
			c.logger.Warnf("Config message was validated against %d, although current config seq has advanced (%d)", msg.GetLastValidationSeq(), seq)
			msg.Payload, _, err = c.support.ProcessConfigMsg(msg.GetPayload())
			if err != nil {
				c.Metrics.ProposalFailures.Add(1)
				return nil, true, errors.Errorf("bad config message: %s", err)
			}
		}

		if c.checkForEvictionNCertRotation(msg.GetPayload()) {

			if !atomic.CompareAndSwapUint32(&c.leadershipTransferInProgress, 0, 1) {
				c.logger.Warnf("A reconfiguration transaction is already in progress, ignoring a subsequent transaction")
				return
			}

			go func() {
				defer atomic.StoreUint32(&c.leadershipTransferInProgress, 0)

				abdicated := false
				for attempt := 1; attempt <= AbdicationMaxAttempts; attempt++ {
					if err := c.Node.abdicateLeadership(); err != nil {
						// If there is no leader, abort and do not retry.
						// Return early to prevent re-submission of the transaction

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Read the inner error for the exact ProcessConfigMsg failure.
  2. Re-fetch the latest channel config, re-apply the change with configtxlator, re-sign with current admin certs, and resubmit.
  3. Ensure the submitting admin identity is still a valid channel/orderer admin.
  4. Validate the computed config update with configtxlator before submitting.
Defensive patterns

Strategy: validation

Validate before calling

env := &common.Envelope{...}
if err := proto.Unmarshal(rawTx, env); err != nil { return err }
payload := &common.Payload{}
if err := proto.Unmarshal(env.Payload, payload); err != nil {
    return fmt.Errorf("payload not a valid protobuf Payload: %w", err)
}
if payload.Header == nil || payload.Header.ChannelHeader == nil {
    return errors.New("missing channel header")
}

Type guard

func isWellFormedEnvelope(raw []byte) bool {
    env := &common.Envelope{}
    return proto.Unmarshal(raw, env) == nil && len(env.Payload) > 0
}

Try / catch

if strings.HasPrefix(err.Error(), "bad message:") {
    log.Printf("envelope rejected: %v", err)
    // do not retry; rebuild the transaction
}

Prevention

When it happens

Trigger: A config update envelope signed against an older config seq is submitted; ProcessConfigMsg re-processing fails because the update is invalid against the new config — wrong signature set, modified payload, unsupported change, or malformed ConfigUpdate.

Common situations: Submitting a stale config update after another config change landed on the channel; config update signed by an admin whose certificate was rotated out; hand-built config updates missing required fields.

Related errors


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