hyperledger/fabric · error
unexpected header type: %v
Error message
unexpected header type: %v
What it means
If the block's channel header type is neither HeaderType_ORDERER_TRANSACTION nor HeaderType_CONFIG, ConfigEnvelopeFromBlock returns this error. It means the block passed in is not a configuration-related envelope — e.g. a regular transaction, peer-chaincode invocation, or config-update envelope was supplied where a config envelope was required.
Source
Thrown at orderer/consensus/etcdraft/util.go:172
}
envelope, err := protoutil.ExtractEnvelope(block, 0)
if err != nil {
return nil, errors.Wrapf(err, "failed to extract envelope from the block")
}
channelHeader, err := protoutil.ChannelHeader(envelope)
if err != nil {
return nil, errors.Wrap(err, "cannot extract channel header")
}
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")
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Guard the call with protoutil.IsConfigBlock(block) (as ConsensusMetadataFromConfigBlock does) before parsing.
- Locate the latest config block via the ledger's config manager (or the block whose header type is HeaderType_CONFIG) and pass that one.
- Check channelHeader.Type before invoking to give a clearer failure message.
Example fix
// before
block, _ := ledger.GetBlockByNumber(currentHeight - 1)
meta, _, err := ConsensusMetadataFromConfigBlock(block) // may be a tx block
// after
block, _ := ledger.GetBlockByNumber(currentHeight - 1)
if !protoutil.IsConfigBlock(block) {
return errors.New("expected config block, got regular block")
}
meta, _, err := ConsensusMetadataFromConfigBlock(block) Defensive patterns
Strategy: validation
Validate before calling
hdr, err := ConfigChannelHeader(block)
if err != nil {
return err
}
if hdr.GetType() != int32(common.HeaderType_CONFIG) {
return errors.Errorf("block type %d is not a config block", hdr.GetType())
} Type guard
func isConfigTypedBlock(block *common.Block) bool {
hdr, err := ConfigChannelHeader(block)
return err == nil && hdr.GetType() == int32(common.HeaderType_CONFIG)
} Try / catch
envelope, err := ConfigEnvelopeFromBlock(block)
if err != nil {
if strings.Contains(err.Error(), "unexpected header type") {
logger.Warnf("skipping non-config block %d", block.Header.Number)
return nil
}
return err
} Prevention
- Filter blocks with protoutil.IsConfigBlock before any config parsing.
- Identify the latest config block via the ledger config manager rather than guessing block numbers.
- When replaying block events, dispatch on channelHeader.Type.
When it happens
Trigger: Calling ConsensusMetadataFromConfigBlock with a non-config block (HeaderType_TRANSACTION/ENDORSER_TRANSACTION etc.), typically because a normal application block was mistaken for a config block.
Common situations: Subscribing to block events and forwarding every block to config parsing without checking IsConfigBlock; passing the wrong block number (a tx block) during channel onboarding or raft metadata inspection.
Related errors
- cannot extract channel header
- nil block
- failed to extract payload from config envelope
- cannot load client cert for consenter %s:%d: %s
- cannot load server cert for consenter %s:%d: %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/9db01658f150b99b.
Report an issue: GitHub.