{"record":{"id":"191631fc7cc10ea5","repo":"hyperledger/fabric","slug":"epoch-is-non-zero","errorCode":null,"errorMessage":"epoch is non-zero","messagePattern":"epoch is non-zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/endorser/msgvalidation.go","lineNumber":143,"sourceCode":"\t\tChannelID: up.ChannelHeader.ChannelId,\n\t\tTxID:      up.TxID(),\n\t})\n\n\t// validate the header type\n\tswitch common.HeaderType(up.ChannelHeader.Type) {\n\tcase common.HeaderType_ENDORSER_TRANSACTION:\n\tcase common.HeaderType_CONFIG:\n\t\t// The CONFIG transaction type has _no_ business coming to the propose API.\n\t\t// In fact, anything coming to the Propose API is by definition an endorser\n\t\t// transaction, so any other header type seems like it ought to be an error... oh well.\n\n\tdefault:\n\t\treturn errors.Errorf(\"invalid header type %s\", common.HeaderType(up.ChannelHeader.Type))\n\t}\n\n\t// ensure the epoch is 0\n\tif up.ChannelHeader.Epoch != 0 {\n\t\treturn errors.Errorf(\"epoch is non-zero\")\n\t}\n\n\t// ensure that there is a nonce\n\tif len(up.SignatureHeader.Nonce) == 0 {\n\t\treturn errors.Errorf(\"nonce is empty\")\n\t}\n\n\t// ensure that there is a creator\n\tif len(up.SignatureHeader.Creator) == 0 {\n\t\treturn errors.New(\"creator is empty\")\n\t}\n\n\texpectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator)\n\tif up.TxID() != expectedTxID {\n\t\treturn errors.Errorf(\"incorrectly computed txid '%s' -- expected '%s'\", up.TxID(), expectedTxID)\n\t}\n\n\tif up.SignedProposal.ProposalBytes == nil {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/endorser/msgvalidation.go#L125-L161","documentation":"The endorser requires the proposal's ChannelHeader.Epoch to be zero. Epoch is reserved for future reconfiguration semantics in Fabric and non-zero values are rejected to prevent replay/reconfiguration confusion. Any proposal carrying a non-zero epoch is invalid.","triggerScenarios":"ProcessProposal -> preProcess -> Validate on a SignedProposal where up.ChannelHeader.Epoch != 0 — usually a manually built channel header with Epoch set from another system (e.g. Kafka epoch, view number).","commonSituations":"Custom client code that fills ChannelHeader fields generically and sets Epoch, ported code from other blockchain SDKs where 'epoch' means something else, mutated/replayed messages.","solutions":["Leave ChannelHeader.Epoch as 0 (do not set it) when constructing proposals.","If copying headers from other messages, explicitly zero the Epoch field.","Use a supported Fabric SDK to construct proposals rather than hand-writing channel headers."],"exampleFix":"// before\nchdr := &common.ChannelHeader{Type: 1, TxId: txid, Epoch: 5}\n// after\nchdr := &common.ChannelHeader{Type: 1, TxId: txid, Epoch: 0}","handlingStrategy":"validation","validationCode":"if hdr.Epoch != 0 {\n    return errors.New(\"epoch must be 0 for endorsement proposals\")\n}","typeGuard":"func hasZeroEpoch(h *common.ChannelHeader) bool {\n    return h != nil && h.Epoch == 0\n}","tryCatchPattern":null,"preventionTips":["Never set ChannelHeader.Epoch manually","Zero all header fields you don't understand when copying headers","Use SDK builders instead of hand-written headers"],"tags":["hyperledger-fabric","endorser","header-validation"],"backgroundTag":"non-zero-epoch","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"}