hyperledger/fabric · critical
proposal hash does not match
Error message
proposal hash does not match
What it means
For each action, the validator recomputes the proposal hash (pHash) from the chaincode proposal payload and compares it with the ProposalHash carried in the ProposalResponsePayload. A mismatch means the endorsement does not correspond to the proposal bytes in the transaction, so the action is invalid.
Source
Thrown at core/common/validation/msgvalidation.go:240
// extract the proposal response payload
prp, err := protoutil.UnmarshalProposalResponsePayload(ccActionPayload.Action.ProposalResponsePayload)
if err != nil {
return err
}
// build the original header by stitching together
// the common ChannelHeader and the per-action SignatureHeader
hdrOrig := &common.Header{ChannelHeader: hdr.ChannelHeader, SignatureHeader: act.Header}
// compute proposalHash
pHash, err := protoutil.GetProposalHash2(hdrOrig, ccActionPayload.ChaincodeProposalPayload)
if err != nil {
return err
}
// ensure that the proposal hash matches
if !bytes.Equal(pHash, prp.ProposalHash) {
return errors.New("proposal hash does not match")
}
}
return nil
}
// ValidateTransaction checks that the transaction envelope is properly formed
func ValidateTransaction(e *common.Envelope, cryptoProvider bccsp.BCCSP) (*common.Payload, pb.TxValidationCode) {
putilsLogger.Debugf("ValidateTransactionEnvelope starts for envelope %p", e)
// check for nil argument
if e == nil {
putilsLogger.Errorf("Error: nil envelope")
return nil, pb.TxValidationCode_NIL_ENVELOPE
}
// get the payload from the envelope
payload, err := protoutil.UnmarshalPayload(e.Payload)View on GitHub (pinned to 2736b63f8f)
Solutions
- Use the original proposal payload bytes (not re-marshalled ones) when building the transaction so the hash matches
- Ensure the signed proposal submitted for endorsement is the same object used to build the final transaction
- Check for any middleware/SDK that re-encodes the proposal; disable re-marshalling
- If mixing endorsements, confirm all responses came from the same signed proposal
Example fix
// before respBytes, _ := proto.Marshal(&proposal) // re-marshalled, may differ action, _ := utils.CreateTxEndorsement(respBytes, ...) // after action, _ := utils.CreateTxEndorsement(originalProposalBytes, ...) // bytes as signed
Defensive patterns
Strategy: validation
Validate before calling
prp, err := protoutil.UnmarshalProposalResponsePayload(act.Payload)
if err != nil { return err }
if !bytes.Equal(prp.ProposalHash, computedProposalHash) { return errors.New("proposal hash mismatch before submit") } Type guard
func proposalHashMatches(act *common.TransactionAction, proposal []byte) bool {
prp, err := protoutil.UnmarshalProposalResponsePayload(act.Payload)
if err != nil { return false }
h, err := utils.GetProposalHash1(nil, proposal, nil) // per your SDK version
return err == nil && bytes.Equal(h, prp.ProposalHash)
} Try / catch
if err := ValidateTransaction(env, policy); err != nil {
if strings.Contains(err.Error(), "proposal hash does not match") {
// re-endorse: the endorsement doesn't match the proposal bytes
}
return err
} Prevention
- Keep and reuse the exact proposal bytes used for signing — never re-marshal before tx creation
- Use the SDK's createTransaction(proposalResponses) with responses from one proposal
- Avoid proxies/middleware that transform proposal payloads
When it happens
Trigger: ValidateTransaction where prp.ProposalHash differs from the hash recomputed from ccInspection/chaincode proposal payload bytes — e.g. the proposal was modified after endorsement or a response from a different proposal was used.
Common situations: Client mixes proposal responses from different invocations or channels; bytes are altered by a proxy/SDK between endorsement and tx creation; hash computed over a differently-encoded payload (field ordering/extra fields); SDK version changes changing serialization.
Related errors
- implicit policy evaluation failed - %d sub-policies were sat
- error unmarshalling ChaincodeHeaderExtension
- nil ChaincodeId in header extension
- nil ChaincodeId in ChaincodeAction
- invalid chaincode ID
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/e32c09e1844a28fa.
Report an issue: GitHub.