hyperledger/fabric · error
ProposalResponsePayloads do not match (base64): '%s' vs '%s'
Error message
ProposalResponsePayloads do not match (base64): '%s' vs '%s'
What it means
When multiple proposal responses are supplied, CreateSignedTx requires all ProposalResponsePayloads to be bitwise identical so a deterministic transaction can be built. If any payload differs (different read-write set, different result), the error reports both payloads base64-encoded for comparison.
Source
Thrown at protoutil/txutils.go:191
if !bytes.Equal(signerBytes, shdr.Creator) {
return nil, errors.New("signer must be the same as the one referenced in the header")
}
// ensure that all actions are bitwise equal and that they are successful
var a1 []byte
for n, r := range resps {
if r.Response.Status < 200 || r.Response.Status >= 400 {
return nil, errors.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
}
if n == 0 {
a1 = r.Payload
continue
}
if !bytes.Equal(a1, r.Payload) {
return nil, errors.Errorf("ProposalResponsePayloads do not match (base64): '%s' vs '%s'",
b64.StdEncoding.EncodeToString(r.Payload), b64.StdEncoding.EncodeToString(a1))
}
}
// fill endorsements according to their uniqueness
endorsersUsed := make(map[string]struct{})
var endorsements []*peer.Endorsement
for _, r := range resps {
if r.Endorsement == nil {
continue
}
key := string(r.Endorsement.Endorser)
if _, used := endorsersUsed[key]; used {
continue
}
endorsements = append(endorsements, r.Endorsement)
endorsersUsed[key] = struct{}{}
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Make the chaincode deterministic: no randomness, map-range-order dependence, wall-clock time, or external calls in transactions
- Ensure all endorsers are at the same ledger height before endorsement (retry or wait for sync)
- Collect responses from a single proposal instance (same txid/nonce) rather than mixing proposals
- Diff the base64 payloads in the error to identify the divergent field
Example fix
// before
// chaincode nondeterminism
for k := range stateMap { results[k] = stateMap[k] } // map order varies per peer
// after
keys := make([]string, 0, len(stateMap))
for k := range stateMap { keys = append(keys, k) }
sort.Strings(keys)
for _, k := range keys { results[k] = stateMap[k] } Defensive patterns
Strategy: validation
Validate before calling
func payloadsIdentical(resps []*peer.ProposalResponse) error {
if len(resps) < 2 { return nil }
first := resps[0].Payload
for _, r := range resps[1:] {
if !bytes.Equal(first, r.Payload) {
return fmt.Errorf("divergent endorsement payloads")
}
}
return nil
} Try / catch
if err := payloadsIdentical(resps); err != nil {
// endorsers disagree: resync peers or fix chaincode determinism, then retry
return nil, err
}
tx, err := protoutil.CreateSignedTx(proposal, signer, resps...) Prevention
- Write deterministic chaincode: no map-iteration order, no time.Now(), no randomness
- Ensure endorsing peers are in sync (same block height) before endorsing
- Always use responses from a single proposal instance
When it happens
Trigger: Endorsers returned divergent results for the same proposal — different read/write sets, nondeterministic chaincode (map iteration, timestamps, randomness), or queries executed at different block heights.
Common situations: Nondeterministic chaincode using Go map iteration order or time.Now(); endorsers on different ledger states (one behind); using responses collected at different times or from proposals with differing nonces; CouchDB vs LevelDB divergence.
Related errors
- failed constructing descriptor for %v
- proposal response was not successful, error code %d, msg %s
- First message needs to be a register
- TLS is active but chaincode %s didn't send certificate
- Chaincode %s with given certificate hash %v not found in reg
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/5d439e68e15dc1a7.
Report an issue: GitHub.