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 transactionView on GitHub (pinned to 2736b63f8f)
Solutions
- Read the inner error for the exact ProcessConfigMsg failure.
- Re-fetch the latest channel config, re-apply the change with configtxlator, re-sign with current admin certs, and resubmit.
- Ensure the submitting admin identity is still a valid channel/orderer admin.
- 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
- Build transactions with a supported SDK only.
- Verify channel name before submit.
- Do not hand-craft protobuf envelopes.
- Pin SDK and Fabric versions.
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
- bad message: %s
- failed to unmarshal new etcdraft metadata configuration
- not a config block
- '%s' not equal <newest|oldest|config|(number)>
- unmarshalling block: %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/60436ec3c52692a4.
Report an issue: GitHub.