hyperledger/fabric · error

LSCC can only issue one or two putState upon deploy

Error message

LSCC can only issue one or two putState upon deploy

What it means

Upon deploy, LSCC's transaction write set may contain at most two putState writes: the chaincode data and (optionally) the collection config. validateRWSetAndCollection rejects the transaction with this policy error if the LSCC read-write set contains more than two writes, since additional writes indicate a malformed or malicious deploy invocation.

Source

Thrown at core/handlers/validation/builtin/v13/lscc_validation_logic.go:232

}

// validateRWSetAndCollection performs validation of the rwset
// of an LSCC deploy operation and then it validates any collection
// configuration
func (vscc *Validator) validateRWSetAndCollection(
	lsccrwset *kvrwset.KVRWSet,
	cdRWSet *ccprovider.ChaincodeData,
	lsccArgs [][]byte,
	lsccFunc string,
	ac vc.Capabilities,
	channelName string,
) commonerrors.TxValidationError {
	/********************************************/
	/* security check 0.a - validation of rwset */
	/********************************************/
	// there can only be one or two writes
	if len(lsccrwset.Writes) > 2 {
		return policyErr(fmt.Errorf("LSCC can only issue one or two putState upon deploy"))
	}

	/**********************************************************/
	/* security check 0.b - validation of the collection data */
	/**********************************************************/
	var collectionsConfigArg []byte
	if len(lsccArgs) > 5 {
		collectionsConfigArg = lsccArgs[5]
	}

	var collectionsConfigLedger []byte
	if len(lsccrwset.Writes) == 2 {
		key := privdata.BuildCollectionKVSKey(cdRWSet.Name)
		if lsccrwset.Writes[1].Key != key {
			return policyErr(fmt.Errorf("invalid key for the collection of chaincode %s:%s; expected '%s', received '%s'",
				cdRWSet.Name, cdRWSet.Version, key, lsccrwset.Writes[1].Key))
		}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Use the standard fabric SDK/CLI to deploy or upgrade chaincode so the LSCC rwset contains only the permitted writes
  2. Do not add extra putState writes to LSCC transactions; chaincode state writes belong to user chaincode, not the lifecycle call
  3. Ensure all peers and clients run a consistent fabric version so the correct lifecycle path (v1 lscc deploy vs v2 _lifecycle) is used
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: do not extend LSCC transactions with extra writes
// use only the official lifecycle APIs (lscc deploy/upgrade or _lifecycle)
if (!usingOfficialLifecycleApi) {
  throw new Error('custom LSCC transaction construction detected; use standard lifecycle APIs');
}

Type guard

function isStandardLifecycleInvocation(tx) {
  return tx.chaincodeId === 'lscc' || tx.chaincodeId === '_lifecycle';
}

Try / catch

try {
  await contract.submitTransaction('DeployChaincode', ...args);
} catch (err) {
  if (String(err).includes('LSCC can only issue one or two putState')) {
    // rebuild the deploy transaction with only the standard LSCC writes
  }
  throw err;
}

Prevention

When it happens

Trigger: A chaincode deploy/upgrade invocation whose LSCC rwset has len(Writes) > 2 — e.g. a crafted proposal writing extra keys through lscc, or tooling that packs additional state mutations into the LSCC transaction.

Common situations: Custom or patched lifecycle tooling injecting extra writes into the LSCC transaction; fabric version mismatch where a peer receives deploy-format transactions it doesn't expect (pre-v2 vs v2 lifecycle); malicious proposals probing the validator.

Related errors


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