hyperledger/fabric · error
expected cc version , found
Error message
expected cc version %s, found %s
What it means
The validator compares the Version in the ChaincodeData produced by lscc against the Version in the submitted ChaincodeDeploymentSpec. If they differ, the write-set does not faithfully reflect the deployment spec and the transaction fails validation. This prevents an attacker from deploying one version while recording another on the ledger.
Solutions
- Regenerate the upgrade proposal with the correct, matching version in both ChaincodeSpec.ChaincodeId.Version and the lscc write value.
- If using custom code, construct ChaincodeData.Version from cds.ChaincodeSpec.ChaincodeId.Version.
- Discard cached signed proposals after changing the chaincode version.
- Verify the chaincode package/version used by install and instantiate steps is the same.
Example fix
// before
cd := &ccprovider.ChaincodeData{Name: name, Version: "1.0", ...}
// after
cd := &ccprovider.ChaincodeData{Name: cds.ChaincodeSpec.ChaincodeId.Name, Version: cds.ChaincodeSpec.ChaincodeId.Version, ...} Defensive patterns
Strategy: validation
Validate before calling
// before submit: version in spec must equal version in lscc record
cd := &ccprovider.ChaincodeData{}
proto.Unmarshal(lsccWriteValue, cd)
if cd.Version != cds.ChaincodeSpec.ChaincodeId.Version {
return fmt.Errorf("spec version %s != lscc record version %s", cds.ChaincodeSpec.ChaincodeId.Version, cd.Version)
} Type guard
func versionMatches(cd *ccprovider.ChaincodeData, specVersion string) bool { return cd != nil && cd.Version == specVersion } Try / catch
if err != nil && strings.Contains(err.Error(), "expected cc version") { /* rebuild proposal with matching version and resubmit */ } Prevention
- Bump version in one place and propagate it to spec, package, and proposal
- Discard cached signed proposals after version changes
- Parameterize versions in deployment scripts
When it happens
Trigger: An upgrade transaction where the new version in ChaincodeSpec.ChaincodeId.Version does not equal the Version serialized into the lscc ChaincodeData write — e.g. a stale or mismatched proposal.
Common situations: Custom lifecycle tooling hard-coding version "1.0" while the spec says "2.0", reusing an old signed proposal after bumping the version, SDK builds the ChaincodeData from different metadata than the spec, replaying a previous upgrade transaction.
Related errors
- Existing version of the cc on the ledger
- Existing version of the cc on the ledger
- expected cc name , found
- unmarshalling of ChaincodeData failed, error
- Upgrading non-existent chaincode
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/b8828b478e80ae82.
Report an issue: GitHub.
Appendix: source
Thrown at core/handlers/validation/builtin/v13/lscc_validation_logic.go:479
return policyErr(fmt.Errorf("LSCC must issue at least one single putState upon deploy/upgrade"))
}
// the first key name must be the chaincode id provided in the deployment spec
if lsccrwset.Writes[0].Key != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
return policyErr(fmt.Errorf("expected key %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, lsccrwset.Writes[0].Key))
}
// the value must be a ChaincodeData struct
cdRWSet := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(lsccrwset.Writes[0].Value, cdRWSet)
if err != nil {
return policyErr(fmt.Errorf("unmarshalling of ChaincodeData failed, error %s", err))
}
// the chaincode name in the lsccwriteset must match the chaincode name in the deployment spec
if cdRWSet.Name != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
return policyErr(fmt.Errorf("expected cc name %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, cdRWSet.Name))
}
// the chaincode version in the lsccwriteset must match the chaincode version in the deployment spec
if cdRWSet.Version != cdsArgs.ChaincodeSpec.ChaincodeId.Version {
return policyErr(fmt.Errorf("expected cc version %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Version, cdRWSet.Version))
}
// it must only write to 2 namespaces: LSCC's and the cc that we are deploying/upgrading
for _, ns := range txRWSet.NsRwSets {
if ns.NameSpace != "lscc" && ns.NameSpace != cdRWSet.Name && len(ns.KvRwSet.Writes) > 0 {
return policyErr(fmt.Errorf("LSCC invocation is attempting to write to namespace %s", ns.NameSpace))
}
}
logger.Debugf("Validating %s for cc %s version %s", lsccFunc, cdRWSet.Name, cdRWSet.Version)
switch lsccFunc {
case lscc.DEPLOY:
/******************************************************************/
/* security check 1 - cc not in the LCCC table of instantiated cc */
/******************************************************************/
if ccExistsOnLedger {
return policyErr(fmt.Errorf("Chaincode %s is already instantiated", cdsArgs.ChaincodeSpec.ChaincodeId.Name))View on GitHub (pinned to 2736b63f8f)