{"record":{"id":"409b1c5a9a7fc791","repo":"hyperledger/fabric","slug":"invalid-epoch-in-channelheader-expected-0-got-409b1c","errorCode":null,"errorMessage":"invalid epoch in ChannelHeader. Expected 0, got [%d]","messagePattern":"invalid epoch in ChannelHeader\\. Expected 0, got \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tx/endorser/parser.go","lineNumber":129,"sourceCode":"\treturn &EndorserTx{\n\t\tComputedTxID: computedTxID,\n\t\tChannelID:    txenv.ChannelHeader.ChannelId,\n\t\tCreator:      txenv.SignatureHeader.Creator,\n\t\tResponse:     ccAction.Response,\n\t\tEvents:       ccAction.Events,\n\t\tResults:      ccAction.Results,\n\t\tEndorsements: ccActionPayload.Action.Endorsements,\n\t\tChaincodeID:  hdrExt.ChaincodeId,\n\t\tType:         txenv.ChannelHeader.Type,\n\t\tVersion:      txenv.ChannelHeader.Version,\n\t\tEpoch:        txenv.ChannelHeader.Epoch,\n\t\tNonce:        txenv.SignatureHeader.Nonce,\n\t}, nil\n}\n\nfunc (e *EndorserTx) validate() error {\n\tif e.Epoch != 0 {\n\t\treturn errors.Errorf(\"invalid epoch in ChannelHeader. Expected 0, got [%d]\", e.Epoch)\n\t}\n\n\tif e.Version != 0 {\n\t\treturn errors.Errorf(\"invalid version in ChannelHeader. Expected 0, got [%d]\", e.Version)\n\t}\n\n\tif err := ValidateChannelID(e.ChannelID); err != nil {\n\t\treturn err\n\t}\n\n\tif len(e.Nonce) == 0 {\n\t\treturn errors.New(\"empty nonce\")\n\t}\n\n\tif len(e.Creator) == 0 {\n\t\treturn errors.New(\"empty creator\")\n\t}\n","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/tx/endorser/parser.go#L111-L147","documentation":"The parsed ChannelHeader has a non-zero Epoch field. Fabric endorser transactions must have Epoch set to 0; a non-zero epoch indicates the header was built incorrectly or by non-standard tooling. This parser enforces the Fabric spec requirement strictly.","triggerScenarios":"UnmarshalEndorserTxAndValidate is given an envelope whose ChannelHeader.Epoch != 0 — typically set manually when constructing the header or copied from a config/other header type.","commonSituations":"Custom transaction builders hardcoding an epoch value; headers copied from block/config transactions where epoch carries meaning; ported code from other Fabric components that populate Epoch.","solutions":["Leave ChannelHeader.Epoch unset (zero value) when building the transaction header.","Regenerate the header with protoutil.BuildChannelHeader, which does not set epoch.","If the envelope comes from an external system, have it produce epoch-0 headers."],"exampleFix":"// before\nch := &common.ChannelHeader{ChannelId: \"mychannel\", Epoch: 3}\n// after\nch := &common.ChannelHeader{ChannelId: \"mychannel\"} // Epoch zero value","handlingStrategy":"validation","validationCode":"func hasZeroEpoch(ch *common.ChannelHeader) bool {\n    return ch.GetEpoch() == 0\n}\n// check header before submission: if !hasZeroEpoch(header) { fix header }","typeGuard":"func isEpochZero(header *common.ChannelHeader) bool {\n    return header != nil && header.Epoch == 0\n}","tryCatchPattern":"tx, err := parser.UnmarshalEndorserTxAndValidate(env)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid epoch\") {\n        // rebuild the header with epoch 0 and resubmit\n        return ErrInvalidHeader\n    }\n    return err\n}","preventionTips":["Never set Epoch when constructing endorser transaction headers.","Use protoutil.BuildChannelHeader instead of manual struct literals.","Do not copy headers from config/block transactions into endorser txs."],"tags":["hyperledger-fabric","channel-header","validation"],"backgroundTag":"invalid-channel-header","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"}