hyperledger/fabric · error
Block with id [%d] on channel [%s] does not have metadata. B
Error message
Block with id [%d] on channel [%s] does not have metadata. Block not valid.
What it means
VerifyBlock requires each block to carry metadata (block.Metadata with at least one metadata entry) because signatures and validation info live in the metadata section. A block missing metadata cannot be signed-verified so it is rejected as not valid.
Source
Thrown at internal/peer/gossip/mcs.go:150
blockSeqNum := block.Header.Number
if seqNum != blockSeqNum {
return fmt.Errorf("Claimed seqNum is [%d] but actual seqNum inside block is [%d]", seqNum, blockSeqNum)
}
// - Extract channelID and compare with chainID
channelID, err := protoutil.GetChannelIDFromBlock(block)
if err != nil {
return fmt.Errorf("Failed getting channel id from block with id [%d] on channel [%s]: [%s]", block.Header.Number, chainID, err)
}
if channelID != string(chainID) {
return fmt.Errorf("Invalid block's channel id. Expected [%s]. Given [%s]", chainID, channelID)
}
// - Unmarshal medatada
if block.Metadata == nil || len(block.Metadata.Metadata) == 0 {
return fmt.Errorf("Block with id [%d] on channel [%s] does not have metadata. Block not valid.", block.Header.Number, chainID)
}
dataHash, err := protoutil.BlockDataHash(block.Data)
if err != nil {
return err
}
// - Verify that Header.DataHash is equal to the hash of block.Data
// This is to ensure that the header is consistent with the data carried by this block
if !bytes.Equal(dataHash, block.Header.DataHash) {
return fmt.Errorf("Header.DataHash is different from Hash(block.Data) for block with id [%d] on channel [%s]", block.Header.Number, chainID)
}
return s.verifyHeaderAndMetadata(channelID, block)
}
func (s *MSPMessageCryptoService) verifyHeaderAndMetadata(channelID string, block *pcommon.Block) error {
// Get the policy manager for channelID
cpm := s.channelPolicyManagerGetter.Manager(channelID)View on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure blocks are created/populated via protoutil block-construction helpers that set metadata
- Re-fetch the block from the orderer or a committed source instead of the corrupt copy
- In tests, populate block.Metadata (e.g. with a valid metadata signature array) before verification
Example fix
// before
block := &common.Block{Header: hdr, Data: data}
// after
block := &common.Block{Header: hdr, Data: data, Metadata: &common.BlockMetadata{
Metadata: [][]byte{nil, {}, {}},
}} Defensive patterns
Strategy: type-guard
Validate before calling
func hasMetadata(b *pcommon.Block) bool {
return b != nil && b.Metadata != nil && len(b.Metadata.Metadata) > 0
} Type guard
func hasMetadata(b *pcommon.Block) bool {
return b != nil && b.Metadata != nil && len(b.Metadata.Metadata) > 0
}
if !hasMetadata(block) {
return errors.New("block missing metadata; cannot verify")
}
err := cryptoService.VerifyBlock(chainID, seqNum, block) Try / catch
if err := cryptoService.VerifyBlock(chainID, seqNum, block); err != nil {
if strings.Contains(err.Error(), "does not have metadata") {
log.Warnf("block %d on %s lacks metadata, rejecting", seqNum, chainID)
return nil
}
return err
} Prevention
- Build blocks only via protoutil helpers that populate metadata
- When copying/cloning blocks, include the Metadata field
- Reject headerless/metadata-less blocks at ingestion instead of at verification
When it happens
Trigger: VerifyBlock called with block.Metadata == nil or block.Metadata.Metadata empty — typically a truncated or hand-constructed block, or a block from which metadata was stripped.
Common situations: Blocks synthesized in unit tests without metadata; corruption during block transfer; code that copies only Header/Data when cloning blocks.
Related errors
- Invalid Block on channel [%s]. Header must be different from
- empty block
- block has only %d transactions, but requested tx at position
- no block header
- failed unmarshalling alive message
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/843b3a3c28dea6b0.
Report an issue: GitHub.