hyperledger/fabric · error

no endorsements

Error message

no endorsements

What it means

After deduplicating endorsers, CreateSignedTx requires at least one endorsement to place in the ChaincodeEndorsedAction. Since responses were already required non-empty, this fires in the degenerate case where the collected endorsement set ends up empty.

Source

Thrown at protoutil/txutils.go:212

	}

	// 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{}{}
	}

	if len(endorsements) == 0 {
		return nil, errors.Errorf("no endorsements")
	}

	// create ChaincodeEndorsedAction
	cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}

	// obtain the bytes of the proposal payload that will go to the transaction
	propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl)
	if err != nil {
		return nil, err
	}

	// serialize the chaincode action payload
	cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
	capBytes, err := GetBytesChaincodeActionPayload(cap)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure each ProposalResponse passed in carries a valid Endorsement (signed by an endorser)
  2. Use responses returned directly by the endorser service instead of hand-built ones
  3. In tests, generate responses via a helper that populates Endorsement
  4. If no endorsements can be obtained, fail before attempting to build the tx

Example fix

// before
resp := &peer.ProposalResponse{Response: &peer.Response{Status: 200}} // no Endorsement
env, err := protoutil.CreateSignedTx(proposal, signer, resp)
// after
resp, err := endorser.ProcessProposal(ctx, signedProp)
if err != nil || resp == nil || resp.Endorsement == nil {
    return nil, fmt.Errorf("no endorsement: %w", err)
}
env, err := protoutil.CreateSignedTx(proposal, signer, resp)
Defensive patterns

Strategy: validation

Validate before calling

func hasEndorsement(r *peer.ProposalResponse) error {
    if r == nil || r.Endorsement == nil {
        return fmt.Errorf("response missing endorsement")
    }
    return nil
}

Type guard

func isEndorsed(r *peer.ProposalResponse) bool {
    return r != nil && r.Endorsement != nil
}

Try / catch

for _, r := range resps {
    if err := hasEndorsement(r); err != nil {
        return nil, err
    }
}
tx, err := protoutil.CreateSignedTx(proposal, signer, resps...)

Prevention

When it happens

Trigger: Calling CreateSignedTx with responses whose Endorsement fields are all nil/empty so that no endorsements accumulate, or an effectively empty deduplicated endorsement set.

Common situations: Manually constructed or partially deserialized ProposalResponse objects missing Endorsement; responses built in tests without running real endorsement; upstream code stripping endorsements.

Related errors


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