hyperledger/fabric · error
LSCC can only issue a single putState upon upgrade
Error message
LSCC can only issue a single putState upon upgrade
What it means
For an UPGRADE, LSCC's read-write set must contain exactly one write (the single putState updating the chaincode data on the ledger). VSCC rejects the transaction if the LSCC rwset contains zero or multiple writes, since anything else would indicate a tampered or malformed transaction.
Source
Thrown at core/handlers/validation/builtin/v12/validation_logic.go:703
/**********************************************************/
if cdLedger.Version == cdsArgs.ChaincodeSpec.ChaincodeId.Version {
return policyErr(fmt.Errorf("Existing version of the cc on the ledger (%s) should be different from the upgraded one", cdsArgs.ChaincodeSpec.ChaincodeId.Version))
}
/****************************************************************************/
/* security check 3 validation of rwset (and of collections if enabled) */
/****************************************************************************/
// Only in v1.2, a collection can be updated during a chaincode upgrade
if ac.V1_2Validation() {
// do extra validation for collections
err := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid)
if err != nil {
return err
}
} else {
// there can only be a single ledger write
if len(lsccrwset.Writes) != 1 {
return policyErr(fmt.Errorf("LSCC can only issue a single putState upon upgrade"))
}
}
/*****************************************************/
/* security check 4 - check the instantiation policy */
/*****************************************************/
pol := cdLedger.InstantiationPolicy
if pol == nil {
return policyErr(fmt.Errorf("No instantiation policy was specified"))
}
// FIXME: could we actually pull the cds package from the
// file system to verify whether the policy that is specified
// here is the same as the one on disk?
// PROS: we prevent attacks where the policy is replaced
// CONS: this would be a point of non-determinism
err := vscc.checkInstantiationPolicy(chid, env, pol, payl)
if err != nil {
return errView on GitHub (pinned to 2736b63f8f)
Solutions
- Use the stock LSCC from the matching Fabric release to generate transactions
- Rebuild the proposal with the SDK/CLI so exactly one putState is produced for the upgrade
- If seen unexpectedly, investigate whether a peer or endorser binary is modified/misbehaving
Defensive patterns
Strategy: try-catch
Try / catch
try {
await submitLifecycleTx('upgrade');
} catch (e) {
if (String(e).includes('single putState upon upgrade')) {
// treat as tampered/non-standard tx; rebuild with stock LSCC/SDK
}
} Prevention
- Use stock LSCC and official SDKs to build lifecycle transactions
- Never hand-craft lscc proposal payloads
- Keep peer binaries unmodified and verified
When it happens
Trigger: An lscc UPGRADE transaction whose lsccrwset.Writes has a length != 1 (no writes, or multiple writes), evaluated under v1.2 validation.
Common situations: A tampered or hand-crafted transaction proposal; a modified/corrupted LSCC implementation; malicious endorsement attempting to sneak extra writes into the upgrade.
Related errors
- malformed chaincode invocation spec
- Wrong number of arguments for invocation lscc(%s): expected
- Wrong number of arguments for invocation lscc(%s): received
- txRWSet.FromProtoBytes error %s
- No read write set for lscc was found
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/a91cf0e86ac75a25.
Report an issue: GitHub.