hyperledger/fabric · error

error marshaling ChaincodeInvocationSpec

Error message

error marshaling ChaincodeInvocationSpec

What it means

Same function as above: after the header extension, the ChaincodeInvocationSpec (the chaincode call with arguments) is protobuf-marshaled and failures are wrapped with this message. A failure again points to malformed/nil input or proto incompatibility rather than runtime state.

Source

Thrown at protoutil/proputils.go:70

	if txid == "" {
		txid = ComputeTxID(nonce, creator)
	}

	return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap)
}

// CreateChaincodeProposalWithTxIDNonceAndTransient creates a proposal from
// given input
func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
	ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
	ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
	if err != nil {
		return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension")
	}

	cisBytes, err := proto.Marshal(cis)
	if err != nil {
		return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec")
	}

	ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
	ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
	if err != nil {
		return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
	}

	// TODO: epoch is now set to zero. This must be changed once we
	// get a more appropriate mechanism to handle it in.
	var epoch uint64

	hdr := &common.Header{
		ChannelHeader: MarshalOrPanic(
			&common.ChannelHeader{
				Type:      int32(typ),
				TxId:      txid,
				Timestamp: timestamppb.Now(),

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify go.mod has one consistent github.com/hyperledger/fabric-protos-go version (go mod tidy / go mod graph)
  2. Build cis via peer.ChaincodeSpec factories rather than hand-assembling bytes
  3. Confirm cis.Input args are valid [][]byte (nil elements are fine for proto3, but check for corrupt data)
  4. Re-run with a minimal ChaincodeInvocationSpec to isolate which field breaks marshaling

Example fix

// before
cis := &peer.ChaincodeInvocationSpec{} // no ChaincodeSpec, drifted proto
cis.ProtoSpec = nil
// after
go get github.com/hyperledger/fabric-protos-go@v0.x.y
cis := &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{Type: peer.ChaincodeSpec_GOLANG, ChaincodeId: ccid, Input: &peer.ChaincodeInput{Args: [][]byte{[]byte("invoke")}}}}
Defensive patterns

Strategy: validation

Validate before calling

if cis == nil { return errors.New("cis is nil") }
if cis.ChaincodeSpec == nil { return errors.New("cis.ChaincodeSpec is nil") }
if _, err := proto.Marshal(cis); err != nil { return fmt.Errorf("CIS not marshalable: %w", err) }

Type guard

func cisMarshalable(cis *peer.ChaincodeInvocationSpec) bool {
	if cis == nil { return false }
	_, err := proto.Marshal(cis)
	return err == nil
}

Try / catch

cisBytes, err := proto.Marshal(cis)
if err != nil { return fmt.Errorf("invalid invocation spec, check proto versions: %w", err) }

Prevention

When it happens

Trigger: CreateChaincodeProposalWithTxIDNonceAndTransient (and wrappers CreateChaincodeProposalWithTransient, CreateChaincodeProposalWithTxIDAndTransient, CreateProposalFromCISAndTxid) receiving a cis containing fields the registered proto cannot marshal — typically a fabric-protos package version mismatch or corrupted deep fields like Input bytes.

Common situations: Dependency drift where peer proto descriptors changed between fabric versions; passing hand-built ChaincodeInvocationSpec with invalid nested messages; unregistered custom extensions.

Related errors


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