{"record":{"id":"9db01658f150b99b","repo":"hyperledger/fabric","slug":"unexpected-header-type-v","errorCode":null,"errorMessage":"unexpected header type: %v","messagePattern":"unexpected header type: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":172,"sourceCode":"\t}\n\n\tenvelope, err := protoutil.ExtractEnvelope(block, 0)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to extract envelope from the block\")\n\t}\n\n\tchannelHeader, err := protoutil.ChannelHeader(envelope)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"cannot extract channel header\")\n\t}\n\n\tswitch channelHeader.GetType() {\n\tcase int32(common.HeaderType_ORDERER_TRANSACTION):\n\t\treturn nil, errors.Errorf(\"unsupported legacy system channel header type: %v\", channelHeader.GetType())\n\tcase int32(common.HeaderType_CONFIG):\n\t\treturn envelope, nil\n\tdefault:\n\t\treturn nil, errors.Errorf(\"unexpected header type: %v\", channelHeader.GetType())\n\t}\n}\n\n// ConsensusMetadataFromConfigBlock reads consensus metadata updates from the configuration block\nfunc ConsensusMetadataFromConfigBlock(block *common.Block) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {\n\tif block == nil {\n\t\treturn nil, nil, errors.New(\"nil block\")\n\t}\n\n\tif !protoutil.IsConfigBlock(block) {\n\t\treturn nil, nil, errors.New(\"not a config block\")\n\t}\n\n\tconfigEnvelope, err := ConfigEnvelopeFromBlock(block)\n\tif err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"cannot read config update\")\n\t}\n","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L154-L190","documentation":"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.","triggerScenarios":"Calling ConsensusMetadataFromConfigBlock with a non-config block (HeaderType_TRANSACTION/ENDORSER_TRANSACTION etc.), typically because a normal application block was mistaken for a config block.","commonSituations":"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.","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."],"exampleFix":"// before\nblock, _ := ledger.GetBlockByNumber(currentHeight - 1)\nmeta, _, err := ConsensusMetadataFromConfigBlock(block) // may be a tx block\n\n// after\nblock, _ := ledger.GetBlockByNumber(currentHeight - 1)\nif !protoutil.IsConfigBlock(block) {\n    return errors.New(\"expected config block, got regular block\")\n}\nmeta, _, err := ConsensusMetadataFromConfigBlock(block)","handlingStrategy":"validation","validationCode":"hdr, err := ConfigChannelHeader(block)\nif err != nil {\n    return err\n}\nif hdr.GetType() != int32(common.HeaderType_CONFIG) {\n    return errors.Errorf(\"block type %d is not a config block\", hdr.GetType())\n}","typeGuard":"func isConfigTypedBlock(block *common.Block) bool {\n    hdr, err := ConfigChannelHeader(block)\n    return err == nil && hdr.GetType() == int32(common.HeaderType_CONFIG)\n}","tryCatchPattern":"envelope, err := ConfigEnvelopeFromBlock(block)\nif err != nil {\n    if strings.Contains(err.Error(), \"unexpected header type\") {\n        logger.Warnf(\"skipping non-config block %d\", block.Header.Number)\n        return nil\n    }\n    return err\n}","preventionTips":["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."],"tags":["hyperledger-fabric","etcdraft","block-parsing","header-type"],"backgroundTag":"wrong-block-type","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}