hyperledger/fabric · error · VSCCEndorsementPolicyError

nil pRespPayload.Extension

Error message

nil pRespPayload.Extension

What it means

After decoding the ProposalResponsePayload, VSCC requires a non-nil Extension field, which carries the ChaincodeAction (results, events, response). A nil Extension means the endorser response payload was empty/malformed, so the lscc transaction is rejected as a policy error.

Source

Thrown at core/handlers/validation/builtin/v12/validation_logic.go:567

		// it can't match the name of one of the system chaincodes
		if _, in := systemChaincodeNames[ccName]; in {
			return policyErr(errors.Errorf("chaincode name '%s' is reserved for system chaincodes", ccName))
		}

		// validate chaincode version
		ccVersion := cdsArgs.ChaincodeSpec.ChaincodeId.Version
		// it must comply with the lscc.ChaincodeVersionRegExp
		if !lscc.ChaincodeVersionRegExp.MatchString(ccVersion) {
			return policyErr(errors.Errorf("invalid chaincode version '%s'", ccVersion))
		}

		// get the rwset
		pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload)
		if err != nil {
			return policyErr(fmt.Errorf("GetProposalResponsePayload error %s", err))
		}
		if pRespPayload.Extension == nil {
			return policyErr(fmt.Errorf("nil pRespPayload.Extension"))
		}
		respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension)
		if err != nil {
			return policyErr(fmt.Errorf("GetChaincodeAction error %s", err))
		}
		txRWSet := &rwsetutil.TxRwSet{}
		if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil {
			return policyErr(fmt.Errorf("txRWSet.FromProtoBytes error %s", err))
		}

		// extract the rwset for lscc
		var lsccrwset *kvrwset.KVRWSet
		for _, ns := range txRWSet.NsRwSets {
			logger.Debugf("Namespace %s", ns.NameSpace)
			if ns.NameSpace == "lscc" {
				lsccrwset = ns.KvRwSet
				break
			}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Build the transaction from a genuine endorser response (its Payload already contains the ProposalResponsePayload with Extension populated).
  2. Verify the endorsing peer returned a complete ProposalResponsePayload (inspect with protoutil.UnmarshalProposalResponsePayload locally before signing).
  3. Fix or replace custom payload-assembly code so ChaincodeAction is embedded as the extension.

Example fix

// before
prp := &pb.ProposalResponsePayload{} // Extension empty
// after
prp := &pb.ProposalResponsePayload{
    ProposalHash: hash,
    Extension:    chaincodeActionBytes,
}
Defensive patterns

Strategy: validation

Validate before calling

prp, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload)
if err != nil || prp.Extension == nil {
    return errors.New("proposal response payload missing ChaincodeAction extension")
}

Type guard

func hasChaincodeAction(prp *pb.ProposalResponsePayload) bool {
    return prp != nil && prp.Extension != nil
}

Try / catch

if prp.Extension == nil {
    logger.Warn("endorser returned payload without ChaincodeAction; rejecting tx")
    return policyErr(errors.New("nil pRespPayload.Extension"))
}

Prevention

When it happens

Trigger: The ProposalResponsePayload in an lscc transaction was constructed without its Extension (ChaincodeAction) — e.g. the proposal response was created manually or the endorser returned a payload lacking the extension.

Common situations: Custom transaction builders omitting the extension, endorsers running modified/buggy code, or payload stripping by proxies; also seen in adversarial/fuzzed transactions rejected at commit validation.

Related errors


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