hyperledger/fabric · error

signer must be the same as the one referenced in the header

Error message

signer must be the same as the one referenced in the header

What it means

CreateSignedTx compares the serialized signer identity with the Creator in the proposal's SignatureHeader and rejects mismatched identities. The transaction must be signed by the same identity that originally created the proposal; a different signer would produce an invalid transaction.

Source

Thrown at protoutil/txutils.go:175

	// the original payload
	pPayl, err := UnmarshalChaincodeProposalPayload(proposal.Payload)
	if err != nil {
		return nil, err
	}

	// check that the signer is the same that is referenced in the header
	signerBytes, err := signer.Serialize()
	if err != nil {
		return nil, err
	}

	shdr, err := UnmarshalSignatureHeader(hdr.SignatureHeader)
	if err != nil {
		return nil, err
	}

	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))
		}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Sign the transaction with the same identity that created the proposal (pass the original creator's Signer)
  2. If a different identity must sign, regenerate the proposal with that identity instead of reusing
  3. Verify the MSP identity configuration matches the one used at proposal creation
  4. In tests, construct proposal and signature with the same signer helper

Example fix

// before
otherSigner, _ := adminMSP.GetDefaultSigningIdentity()
env, err := protoutil.CreateSignedTx(proposal, otherSigner, resps...) // proposal made by user
// after
userSigner, err := userMSP.GetDefaultSigningIdentity() // same identity as proposal creator
env, err := protoutil.CreateSignedTx(proposal, userSigner, resps...)
Defensive patterns

Strategy: validation

Validate before calling

func verifySignerMatchesProposal(signer protoutil.Signer, prop *peer.Proposal) error {
    shdr := &common.SignatureHeader{}
    if err := proto.Unmarshal(prop.Header, shdr); err != nil {
        return err
    }
    signerBytes, err := signer.Serialize()
    if err != nil {
        return err
    }
    if !bytes.Equal(signerBytes, shdr.Creator) {
        return fmt.Errorf("signer does not match proposal creator")
    }
    return nil
}

Try / catch

if err := verifySignerMatchesProposal(signer, proposal); err != nil {
    return nil, err
}
tx, err := protoutil.CreateSignedTx(proposal, signer, resps...)

Prevention

When it happens

Trigger: Calling CreateSignedTx with a Signer whose serialized identity (creator bytes) differs from proposal.Header.SignatureHeader.Creator — e.g. a second client instance, a different MSP principal, or a proposal reused across users.

Common situations: Multi-user applications mixing up identities; proposals deserialized and re-signed by a gateway/admin identity; tests reusing a proposal captured from another signer; identity serialization differences across MSP versions/peers.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/909187053f961cad. Report an issue: GitHub.