hyperledger/fabric · error
chaincode invocation spec did not contain chaincode spec
Error message
chaincode invocation spec did not contain chaincode spec
What it means
UnpackProposal successfully decoded the ChaincodeInvocationSpec from the SignedProposal payload, but the decoded spec has a nil ChaincodeSpec field. This means the proposal's input was marshaled as a ChaincodeInvocationSpec wrapper with no actual chaincode spec inside. The endorser rejects it because there is no chaincode to invoke.
Source
Thrown at core/endorser/msgvalidation.go:87
return nil, errors.Errorf("ChaincodeHeaderExtension.ChaincodeId is nil")
}
if chaincodeHdrExt.ChaincodeId.Name == "" {
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 txView on GitHub (pinned to 2736b63f8f)
Solutions
- Build the proposal payload with an SDK helper (fabprotos ChaincodeInvocationSpec populated with Type, ChaincodeId and Input) instead of constructing protobufs by hand.
- Verify ChaincodeInvocationSpec.ChaincodeSpec is non-nil and ChaincodeSpec.ChaincodeId.Name is set before marshaling the payload.
- Regenerate/refresh the proposal with the matching fabric-protos version used by the peer to avoid serialization drift.
Example fix
// before
invocationSpec := &pb.ChaincodeInvocationSpec{}
payload.Input, _ = proto.Marshal(invocationSpec)
// after
invocationSpec := &pb.ChaincodeInvocationSpec{
ChaincodeSpec: &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_NODE,
ChaincodeId: &pb.ChaincodeID{Name: "mycc"},
Input: &pb.ChaincodeInput{Args: args},
},
}
payload.Input, _ = proto.Marshal(invocationSpec) Defensive patterns
Strategy: validation
Validate before calling
var cis pb.ChaincodeInvocationSpec
if err := proto.Unmarshal(payload.Input, &cis); err != nil { return err }
if cis.ChaincodeSpec == nil || cis.ChaincodeSpec.ChaincodeId == nil {
return errors.New("payload has no ChaincodeSpec")
} Type guard
func hasChaincodeSpec(cis *pb.ChaincodeInvocationSpec) bool {
return cis != nil && cis.ChaincodeSpec != nil
} Prevention
- Always build payloads via SDK proposal helpers
- Unit-test payload marshaling before submitting to the peer
- Pin fabric-protos versions between client and peer
When it happens
Trigger: Submitting a SignedProposal via ProcessProposal whose ChaincodeProposalPayload.Input unmarshals to a ChaincodeInvocationSpec with cis.ChaincodeSpec == nil — e.g. the client built the payload with an empty/zero ChaincodeInvocationSpec or deserialized and re-marshaled a spec incorrectly.
Common situations: Hand-crafted proposal payloads in tests or scripts, clients using low-level protobuf APIs instead of the fabric-sdk helper (e.g. newInvokeProposal), corrupted or truncated payloads, or SDK version mismatches that serialize the spec differently.
Related errors
- chaincode input did not contain any input
- empty proposal bytes
- ChaincodeHeaderExtension.ChaincodeId is nil
- ChaincodeHeaderExtension.ChaincodeId.Name is empty
- invalid header type %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/0f17a83100edfe74.
Report an issue: GitHub.