hyperledger/fabric · error
incorrectly computed txid '%s' -- expected '%s'
Error message
incorrectly computed txid '%s' -- expected '%s'
What it means
The TxID embedded in the ChannelHeader must equal protoutil.ComputeTxID(nonce, creator), i.e. the SHA-256 hash of nonce concatenated with creator. This binds the transaction ID to the authentication material and prevents ID spoofing or replay under a different ID. A mismatch means the client computed or copied the TxID incorrectly.
Source
Thrown at core/endorser/msgvalidation.go:158
// ensure the epoch is 0
if up.ChannelHeader.Epoch != 0 {
return errors.Errorf("epoch is non-zero")
}
// ensure that there is a nonce
if len(up.SignatureHeader.Nonce) == 0 {
return errors.Errorf("nonce is empty")
}
// ensure that there is a creator
if len(up.SignatureHeader.Creator) == 0 {
return errors.New("creator is empty")
}
expectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator)
if up.TxID() != expectedTxID {
return errors.Errorf("incorrectly computed txid '%s' -- expected '%s'", up.TxID(), expectedTxID)
}
if up.SignedProposal.ProposalBytes == nil {
return errors.Errorf("empty proposal bytes")
}
if up.SignedProposal.Signature == nil {
return errors.Errorf("empty signature bytes")
}
// get the identity of the creator
creator, err := idDeserializer.DeserializeIdentity(up.SignatureHeader.Creator)
if err != nil {
logger.Warnw("access denied", "error", err, "identity", protoutil.LogMessageForSerializedIdentity(up.SignatureHeader.Creator))
return errors.Errorf("access denied: channel [%s] creator org unknown, creator is malformed", up.ChannelID())
}
genericAuthError := errors.Errorf("access denied: channel [%s] creator org [%s]", up.ChannelID(), creator.GetMSPIdentifier())View on GitHub (pinned to 2736b63f8f)
Solutions
- Recompute TxID with protoutil.ComputeTxID(nonce, creator) (sha256(nonce || creator), hex-encoded) at proposal-build time.
- Generate the nonce and compute the TxID in the same code path so they stay in sync; never reuse a TxID across proposals.
- Prefer SDK proposal builders which guarantee TxID/nonce consistency.
Example fix
// before nonce := randomNonce() txid := previousTxid // reused // after nonce := randomNonce() txid := protoutil.ComputeTxID(nonce, creator)
Defensive patterns
Strategy: validation
Validate before calling
expected := protoutil.ComputeTxID(shdr.Nonce, shdr.Creator)
if hdr.TxId != expected {
return fmt.Errorf("txid mismatch: got %s want %s", hdr.TxId, expected)
} Type guard
func txidMatches(h *common.ChannelHeader, sh *common.SignatureHeader) bool {
return h != nil && sh != nil && h.TxId == protoutil.ComputeTxID(sh.Nonce, sh.Creator)
} Prevention
- Compute TxID from the exact nonce+creator in the same builder function
- Never reuse or hardcode TxIDs
- On retry, regenerate nonce and TxID together
When it happens
Trigger: ProcessProposal -> preProcess -> Validate on a SignedProposal where ChannelHeader.TxId differs from ComputeTxID(SignatureHeader.Nonce, SignatureHeader.Creator) — e.g. TxID reused from a previous tx, nonce regenerated after TxID computation, or TxID computed with a different hash/concat order.
Common situations: Custom clients that compute TxID themselves (wrong algorithm or wrong field order), retry logic that refreshes the nonce but not the TxID, clients copying TxIDs from gateway logs, Fabric upgrade changing ComputeTxID expectations (sha256).
Related errors
- duplicate transaction found [%s]. Creator [%x]
- chaincode invocation spec did not contain chaincode spec
- chaincode input did not contain any input
- invalid header type %s
- epoch is non-zero
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/2a17600698da915f.
Report an issue: GitHub.