hyperledger/fabric · error

failed getting a new bundle from envelope of config block

Error message

failed getting a new bundle from envelope of config block

What it means

In smartbft's remoteNodesFromConfigBlock, the genesis/config block's first envelope is unmarshalled into a cb.Envelope and then passed to channelconfig.NewBundleFromEnvelope, which validates the channel configuration and builds a config bundle. This error is a wrap of the underlying NewBundleFromEnvelope failure, meaning the config envelope is not valid channel configuration (bad payload, missing Channel header, or invalid ConfigGroup structure). It aborts building the SmartBFT cluster metadata after a config block commit.

Source

Thrown at orderer/consensus/smartbft/util.go:289

		return nil, errors.WithMessage(err, "error unmarshalling channel header")
	}

	return &request{
		chHdr:    chdr,
		sigHdr:   sigHdr,
		envelope: envelope,
	}, nil
}

// remoteNodesFromConfigBlock unmarshalls the node config from the block metadata
func remoteNodesFromConfigBlock(block *cb.Block, logger *flogging.FabricLogger, bccsp bccsp.BCCSP) (*nodeConfig, error) {
	env := &cb.Envelope{}
	if err := proto.Unmarshal(block.Data.Data[0], env); err != nil {
		return nil, errors.Wrap(err, "failed unmarshalling envelope of config block")
	}
	bundle, err := channelconfig.NewBundleFromEnvelope(env, bccsp)
	if err != nil {
		return nil, errors.Wrap(err, "failed getting a new bundle from envelope of config block")
	}

	channelMSPs, err := bundle.MSPManager().GetMSPs()
	if err != nil {
		return nil, errors.Wrap(err, "failed obtaining MSPs from MSPManager")
	}

	oc, ok := bundle.OrdererConfig()
	if !ok {
		return nil, errors.New("no orderer config in config block")
	}

	_, err = createSmartBftConfig(oc)
	if err != nil {
		return nil, err
	}

	var nodeIDs []uint64

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Inspect the underlying wrapped error in the log to identify whether unmarshalling, payload extraction, or config validation failed
  2. Verify the block's first envelope is a config transaction (Header type = CONFIG) using protoutil.EnvelopeToPayload / configtx filter
  3. Regenerate the config block with configtxgen/configtxlator matching your Fabric version instead of manually editing it
  4. Check that all peer/orderer binaries and channel config version are compatible (config schema changes between releases)
  5. If block storage is suspect, repair the node by re-fetching the channel genesis/config block from a healthy orderer or re-join the channel

Example fix

// before: committing a block with a non-config envelope at index 0
block.Data.Data[0] = normalTxEnvelopeBytes

// after: ensure index 0 is the CONFIG envelope (e.g. re-created via configtxgen/configtxlator)
configEnv := protoutil.MarshalOrPanic(&cb.Envelope{Payload: configPayloadBytes})
block.Data.Data = [][]byte{configEnv}
Defensive patterns

Strategy: validation

Validate before calling

env := &cb.Envelope{}
if err := proto.Unmarshal(block.Data.Data[0], env); err != nil {
    return fmt.Errorf("block %d: first envelope not unmarshallable: %w", block.Header.Number, err)
}
payload, err := protoutil.UnmarshalPayload(env.Payload)
if err != nil {
    return fmt.Errorf("envelope payload invalid: %w", err)
}
chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil || chdr.Type != int32(cb.HeaderType_CONFIG) {
    return fmt.Errorf("first envelope is not a CONFIG transaction (type=%d)", chdr.Type)
}
// only then call into consensus / configBlockCommitted

Type guard

func isConfigBlock(block *cb.Block) bool {
    if block == nil || len(block.Data.Data) == 0 {
        return false
    }
    env := &cb.Envelope{}
    if proto.Unmarshal(block.Data.Data[0], env) != nil {
        return false
    }
    payload, err := protoutil.UnmarshalPayload(env.Payload)
    if err != nil || payload.Header == nil {
        return false
    }
    chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
    return err == nil && chdr.Type == int32(cb.HeaderType_CONFIG)
}

Try / catch

block, err := ledger.GetConfigBlock()
if err != nil {
    return fmt.Errorf("cannot read config block: %w", err)
}
if !isConfigBlock(block) {
    return errors.New("retrieved block is not a valid config block; regenerate genesis block via configtxgen")
}

Prevention

When it happens

Trigger: configBlockCommitted -> remoteNodesFromConfigBlock with a block whose Data.Data[0] is an envelope that fails channelconfig.NewBundleFromEnvelope: config envelope not extractable, payload type not CONFIG, ConfigGroup missing Channel/Orderer groups, or malformed nested config values.

Common situations: Hand-crafting or mutating a config block (e.g. via configtxlator round-trips gone wrong); committing a block whose first envelope is not a config transaction (e.g. a normal tx at index 0); corrupted block storage; blocks produced by a different Fabric version with incompatible config schemas.

Related errors


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