{"record":{"id":"2f66481fc5c5877f","repo":"hyperledger/fabric","slug":"nonce-is-empty","errorCode":null,"errorMessage":"nonce is empty","messagePattern":"nonce is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/endorser/msgvalidation.go","lineNumber":148,"sourceCode":"\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 {\n\t\treturn errors.Errorf(\"empty proposal bytes\")\n\t}\n\n\tif up.SignedProposal.Signature == nil {\n\t\treturn errors.Errorf(\"empty signature bytes\")","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/endorser/msgvalidation.go#L130-L166","documentation":"Every endorsement proposal must carry a nonce in its SignatureHeader; the nonce is a random value combined with the creator to form a unique transaction ID and prevent replay. An empty nonce makes the proposal invalid.","triggerScenarios":"ProcessProposal -> preProcess -> Validate on a SignedProposal whose SignatureHeader.Nonce is a zero-length byte slice — typically a manually assembled SignatureHeader without setting Nonce.","commonSituations":"Hand-rolled protobuf client code, test fixtures with empty signature headers, clients that deserialize a proposal and rebuild it dropping the nonce.","solutions":["Generate a cryptographically random nonce (e.g. 24 bytes from crypto/rand) and set it in the SignatureHeader before signing.","Use SDK proposal helpers (newProposal / ChaincodeProposalFactory) which generate the nonce automatically.","Verify the serialized SignatureHeader used for signing matches the one embedded in the proposal."],"exampleFix":"// before\nshdr := &common.SignatureHeader{Creator: creator}\n// after\nnonce := make([]byte, 24)\nrand.Read(nonce)\nshdr := &common.SignatureHeader{Creator: creator, Nonce: nonce}","handlingStrategy":"validation","validationCode":"if len(shdr.Nonce) == 0 {\n    return errors.New(\"nonce required: generate 24 random bytes via crypto/rand\")\n}","typeGuard":"func hasNonce(sh *common.SignatureHeader) bool {\n    return sh != nil && len(sh.Nonce) > 0\n}","tryCatchPattern":null,"preventionTips":["Generate a fresh crypto/rand nonce per proposal","Let the SDK generate nonce and TxID together","Never rebuild a SignatureHeader from a deserialized proposal without preserving the nonce"],"tags":["hyperledger-fabric","endorser","signature-header","replay-protection"],"backgroundTag":"empty-nonce","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"}