hyperledger/fabric · error
signed chaincode deployment spec cannot be nil in a package
Error message
signed chaincode deployment spec cannot be nil in a package
What it means
Within ValidateCC, after confirming the package was initialized, the code checks that the SignedChaincodeDeploymentSpec actually contains a ChaincodeDeploymentSpec. If sDepSpec.ChaincodeDeploymentSpec is nil, the package is structurally invalid — it claims to be a signed deployment spec but carries no deployment payload. This is more specific than the 'uninitialized package' case.
Source
Thrown at core/common/ccprovider/sigcdspackage.go:188
// compute the id
hash.Write(scdsdata.CodeHash)
hash.Write(scdsdata.MetaDataHash)
hash.Write(scdsdata.SignatureHash)
id := hash.Sum(nil)
return b, id, scdsdata, nil
}
// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *SignedCDSPackage) ValidateCC(ccdata *ChaincodeData) error {
if ccpack.sDepSpec == nil {
return errors.New("uninitialized package")
}
if ccpack.sDepSpec.ChaincodeDeploymentSpec == nil {
return errors.New("signed chaincode deployment spec cannot be nil in a package")
}
if ccpack.depSpec == nil {
return errors.New("chaincode deployment spec cannot be nil in a package")
}
// This is a hack. LSCC expects a specific LSCC error when names are invalid so it
// has its own validation code. We can't use that error because of import cycles.
// Unfortunately, we also need to check if what have makes some sort of sense as
// protobuf will gladly deserialize garbage and there are paths where we assume that
// a successful unmarshal means everything works but, if it fails, we try to unmarshal
// into something different.
if !isPrintable(ccdata.Name) {
return fmt.Errorf("invalid chaincode name: %q", ccdata.Name)
}
if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)View on GitHub (pinned to 2736b63f8f)
Solutions
- Regenerate the package ensuring ChaincodeDeploymentSpec is populated (proto.Marshal of a real ChaincodeDeploymentSpec) before signing.
- Validate input bytes by unmarshalling into SignedChaincodeDeploymentSpec and checking ChaincodeDeploymentSpec != nil before calling GetCCPackage.
- Fix the producing tool/SDK so it always embeds the deployment spec in the signed wrapper.
- Reject untrusted payloads upstream with a clear message instead of letting GetCCPackage report the generic validation failure.
Example fix
// before
scds := &pb.SignedChaincodeDeploymentSpec{InstantiationPolicy: policy}
// after
cdsBytes, _ := proto.Marshal(cds)
scds := &pb.SignedChaincodeDeploymentSpec{ChaincodeDeploymentSpec: cdsBytes, InstantiationPolicy: policy} Defensive patterns
Strategy: validation
Validate before calling
var scds pb.SignedChaincodeDeploymentSpec
if err := proto.Unmarshal(bytes, &scds); err != nil {
return err
}
if scds.ChaincodeDeploymentSpec == nil {
return errors.New("package rejected: embedded ChaincodeDeploymentSpec is nil")
} Type guard
func hasDeploymentSpec(scds *pb.SignedChaincodeDeploymentSpec) bool {
return scds != nil && scds.ChaincodeDeploymentSpec != nil && scds.ChaincodeDeploymentSpec.ChaincodeSpec != nil
} Try / catch
if err := pack.ValidateCC(ccdata); err != nil {
if strings.Contains(err.Error(), "signed chaincode deployment spec cannot be nil") {
// regenerate the package with an embedded deployment spec
}
return err
} Prevention
- Always populate ChaincodeDeploymentSpec (proto.Marshal of a real CDS) in the signed wrapper before signing.
- Round-trip test: marshal, unmarshal, and assert the embedded spec is present before uploading.
- Reject untrusted packages whose embedded ChaincodeDeploymentSpec is nil.
- Keep SDK/tool versions aligned with the Fabric package format.
When it happens
Trigger: ValidateCC (via GetCCPackage) is given a package initialized from bytes that decode to a SignedChaincodeDeploymentSpec whose ChaincodeDeploymentSpec field is unset — e.g. a serialized protobuf with only metadata/owner fields populated.
Common situations: Hand-crafted or truncated protobuf payloads in uploads/tests; SDKs serializing the wrapper message without embedding the deployment spec; wrong-field assignment when constructing SignedChaincodeDeploymentSpec programmatically.
Related errors
- unknown operation type
- instantiation policy cannot be nil for chaincode (%s:%s)
- uninitialized package
- invalid chaincode name: %q
- failed to unmarshal envelope from bytes
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/e68263ac652280c9.
Report an issue: GitHub.