{"record":{"id":"22c342cb46c8d473","repo":"hyperledger/fabric","slug":"invalid-header-type-s-22c342","errorCode":null,"errorMessage":"invalid header type %s","messagePattern":"invalid header type (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/endorser/msgvalidation.go","lineNumber":138,"sourceCode":"\t}, nil\n}\n\nfunc (up *UnpackedProposal) Validate(idDeserializer msp.IdentityDeserializer) error {\n\tlogger := decorateLogger(endorserLogger, &ccprovider.TransactionParams{\n\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)","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/endorser/msgvalidation.go#L120-L156","documentation":"Validate() checks the proposal's channel header Type; only ENDORSER_TRANSACTION (and tolerated CONFIG reads in legacy paths) may reach the Propose API. Any other header type (e.g. CONFIG_UPDATE, TRANSACTION, QUERY) is rejected because proposals to the endorser are by definition endorsement requests.","triggerScenarios":"ProcessProposal -> preProcess -> Validate on a SignedProposal whose ChannelHeader.Type is not ENDORSER_TRANSACTION — e.g. reusing a config-update envelope or an already-committed transaction envelope as a proposal.","commonSituations":"Feeding orderer envelopes (BlockData transactions) back into the peer's ProcessProposal, misconfigured custom clients setting the header type manually, replaying fabric messages across the wrong APIs.","solutions":["Ensure the proposal is built as an ENDORSER_TRANSACTION (ChannelHeader.Type = HeaderType_ENDORSER_TRANSACTION), which SDKs do automatically.","Do not submit config-update or orderer envelopes to the endorser Propose API; use the orderer/system channel APIs instead.","Check that custom protobuf assembly code sets the header type explicitly to 1 (ENDORSER_TRANSACTION)."],"exampleFix":"// before\nchdr.Type = int32(common.HeaderType_TRANSACTION)\n// after\nchdr.Type = int32(common.HeaderType_ENDORSER_TRANSACTION)","handlingStrategy":"validation","validationCode":"if hdr.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {\n    return fmt.Errorf(\"only ENDORSER_TRANSACTION proposals allowed, got %d\", hdr.Type)\n}","typeGuard":"func isEndorserTx(h *common.ChannelHeader) bool {\n    return h != nil && h.Type == int32(common.HeaderType_ENDORSER_TRANSACTION)\n}","tryCatchPattern":null,"preventionTips":["Never feed orderer/config envelopes into the endorser API","Set header type via SDK constants only","Keep proposal construction in one audited code path"],"tags":["hyperledger-fabric","endorser","header-validation","wrong-message-type"],"backgroundTag":"invalid-header-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"}