hyperledger/fabric · error
not a config block
Error message
not a config block
What it means
ConsensusMetadataFromConfigBlock validates its input with protoutil.IsConfigBlock and returns this error if the block's last envelope is not of config type. The function only reads Raft consensus metadata from configuration blocks, so any ordinary transaction block is rejected.
Source
Thrown at orderer/consensus/etcdraft/util.go:183
switch channelHeader.GetType() {
case int32(common.HeaderType_ORDERER_TRANSACTION):
return nil, errors.Errorf("unsupported legacy system channel header type: %v", channelHeader.GetType())
case int32(common.HeaderType_CONFIG):
return envelope, nil
default:
return nil, errors.Errorf("unexpected header type: %v", channelHeader.GetType())
}
}
// ConsensusMetadataFromConfigBlock reads consensus metadata updates from the configuration block
func ConsensusMetadataFromConfigBlock(block *common.Block) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {
if block == nil {
return nil, nil, errors.New("nil block")
}
if !protoutil.IsConfigBlock(block) {
return nil, nil, errors.New("not a config block")
}
configEnvelope, err := ConfigEnvelopeFromBlock(block)
if err != nil {
return nil, nil, errors.Wrap(err, "cannot read config update")
}
payload, err := protoutil.UnmarshalPayload(configEnvelope.GetPayload())
if err != nil {
return nil, nil, errors.Wrap(err, "failed to extract payload from config envelope")
}
// get config update
configUpdate, err := configtx.UnmarshalConfigUpdateFromPayload(payload)
if err != nil {
return nil, nil, errors.Wrap(err, "could not read config update")
}
return MetadataFromConfigUpdate(configUpdate)View on GitHub (pinned to 2736b63f8f)
Solutions
- Check protoutil.IsConfigBlock(block) at the call site and skip non-config blocks.
- Subscribe to config-update events or filter delivered blocks on HeaderType_CONFIG before parsing.
- Ensure the block passed is the actual latest config block from the channel's config history.
Example fix
// before
for block := range blocks {
meta, _, err := ConsensusMetadataFromConfigBlock(block)
...
}
// after
for block := range blocks {
if !protoutil.IsConfigBlock(block) {
continue // ordinary tx block, not a config update
}
meta, _, err := ConsensusMetadataFromConfigBlock(block)
...
} Defensive patterns
Strategy: validation
Validate before calling
if !protoutil.IsConfigBlock(block) {
return errors.New("input is not a config block")
} Type guard
func isUsableConfigBlock(block *common.Block) bool {
return block != nil && protoutil.IsConfigBlock(block)
} Try / catch
meta, _, err := ConsensusMetadataFromConfigBlock(block)
if err != nil {
if err.Error() == "not a config block" {
return nil // non-config block: safe to ignore
}
return err
} Prevention
- Always gate this call with protoutil.IsConfigBlock.
- Filter delivered blocks on HeaderType_CONFIG in event handlers.
- Fetch config blocks via the ledger's config history, not arbitrary block numbers.
When it happens
Trigger: Passing a regular (transaction) block — not HeaderType_CONFIG — into ConsensusMetadataFromConfigBlock, e.g. forwarding every delivered block instead of only config blocks.
Common situations: Block-deliver handlers that fail to filter on header type; using the wrong block number when inspecting raft config changes; replaying a channel from a snapshot and handing the first non-config block to the parser.
Related errors
- bad message: %s
- bad config message: %s
- cannot read config update
- nil block or nil header
- '%s' not equal <newest|oldest|config|(number)>
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/d7083edbb39d7fa6.
Report an issue: GitHub.