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 err

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Use the stock LSCC from the matching Fabric release to generate transactions
  2. Rebuild the proposal with the SDK/CLI so exactly one putState is produced for the upgrade
  3. 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

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


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