hyperledger/fabric · error

chaincode input did not contain any input

Error message

chaincode input did not contain any input

What it means

The ChaincodeInvocationSpec decoded from the proposal payload exists and has a ChaincodeSpec, but ChaincodeSpec.Input is nil. Input holds the function name and args to pass to the chaincode, so a proposal without it is not executable. The endorser rejects the proposal before any chaincode runs.

Source

Thrown at core/endorser/msgvalidation.go:91

		return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId.Name is empty")
	}

	cpp, err := protoutil.UnmarshalChaincodeProposalPayload(prop.Payload)
	if err != nil {
		return nil, err
	}

	cis, err := protoutil.UnmarshalChaincodeInvocationSpec(cpp.Input)
	if err != nil {
		return nil, err
	}

	if cis.ChaincodeSpec == nil {
		return nil, errors.Errorf("chaincode invocation spec did not contain chaincode spec")
	}

	if cis.ChaincodeSpec.Input == nil {
		return nil, errors.Errorf("chaincode input did not contain any input")
	}

	cppNoTransient := &peer.ChaincodeProposalPayload{Input: cpp.Input, TransientMap: nil}
	ppBytes, err := proto.Marshal(cppNoTransient)
	if err != nil {
		return nil, errors.WithMessage(err, "could not marshal non-transient portion of payload")
	}

	// TODO, this was preserved from the proputils stuff, but should this be BCCSP?

	// The proposal hash is the hash of the concatenation of:
	// 1) The serialized Channel Header object
	// 2) The serialized Signature Header object
	// 3) The hash of the part of the chaincode proposal payload that will go to the tx
	// (ie, the parts without the transient data)
	propHash := sha256.New()
	propHash.Write(hdr.ChannelHeader)
	propHash.Write(hdr.SignatureHeader)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Always populate ChaincodeSpec.Input with at least the function name and args (ChaincodeInput.Args) before signing the proposal.
  2. Check the SDK client call passes a non-empty function/args list (e.g. contract.submitTransaction('fn', ...args)).
  3. If using transient/private data flows, ensure the input is in the proposal, not moved entirely into the transient map.

Example fix

// before
spec := &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "mycc"}}
// after
spec := &pb.ChaincodeSpec{
    ChaincodeId: &pb.ChaincodeID{Name: "mycc"},
    Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("invoke"), []byte("a"), []byte("b"), []byte("1")}},
}
Defensive patterns

Strategy: validation

Validate before calling

if cis.ChaincodeSpec == nil || cis.ChaincodeSpec.Input == nil || len(cis.ChaincodeSpec.Input.Args) == 0 {
    return errors.New("chaincode input/args required")
}

Type guard

func hasChaincodeInput(spec *pb.ChaincodeSpec) bool {
    return spec != nil && spec.Input != nil && len(spec.Input.Args) > 0
}

Prevention

When it happens

Trigger: ProcessProposal -> UnpackProposal receives a SignedProposal whose ChaincodeSpec was populated with ChaincodeId and Type but with Input left nil — e.g. building a query/invoke with no args, or a deploy-type spec without constructor args.

Common situations: SDK calls where the 'args' or 'fcn' parameter was omitted/empty, manually constructed ChaincodeSpec protobufs, chaincode upgrade/install proposals stripped of input during custom tooling transformations.

Related errors


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