{"record":{"id":"f8cac14220d50f49","repo":"hyperledger/fabric","slug":"lscc-can-only-issue-a-single-putstate-upon-deploy","errorCode":null,"errorMessage":"LSCC can only issue a single putState upon deploy","messagePattern":"LSCC can only issue a single putState upon deploy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/handlers/validation/builtin/v12/validation_logic.go","lineNumber":654,"sourceCode":"\t\t\t/* security check 1 - cc not in the LCCC table of instantiated cc */\n\t\t\t/******************************************************************/\n\t\t\tif ccExistsOnLedger {\n\t\t\t\treturn policyErr(fmt.Errorf(\"Chaincode %s is already instantiated\", cdsArgs.ChaincodeSpec.ChaincodeId.Name))\n\t\t\t}\n\n\t\t\t/****************************************************************************/\n\t\t\t/* security check 2 - validation of rwset (and of collections if enabled) */\n\t\t\t/****************************************************************************/\n\t\t\tif ac.PrivateChannelData() {\n\t\t\t\t// do extra validation for collections\n\t\t\t\terr := vscc.validateRWSetAndCollection(lsccrwset, cdRWSet, lsccArgs, lsccFunc, ac, chid)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// there can only be a single ledger write\n\t\t\t\tif len(lsccrwset.Writes) != 1 {\n\t\t\t\t\treturn policyErr(fmt.Errorf(\"LSCC can only issue a single putState upon deploy\"))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*****************************************************/\n\t\t\t/* security check 3 - check the instantiation policy */\n\t\t\t/*****************************************************/\n\t\t\tpol := cdRWSet.InstantiationPolicy\n\t\t\tif pol == nil {\n\t\t\t\treturn policyErr(fmt.Errorf(\"no instantiation policy was specified\"))\n\t\t\t}\n\t\t\t// FIXME: could we actually pull the cds package from the\n\t\t\t// file system to verify whether the policy that is specified\n\t\t\t// here is the same as the one on disk?\n\t\t\t// PROS: we prevent attacks where the policy is replaced\n\t\t\t// CONS: this would be a point of non-determinism\n\t\t\terr := vscc.checkInstantiationPolicy(chid, env, pol, payl)\n\t\t\tif err != nil {\n\t\t\t\treturn err","sourceCodeStart":636,"sourceCodeEnd":672,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/handlers/validation/builtin/v12/validation_logic.go#L636-L672","documentation":"When private channel data (collections) is not enabled, lscc deploy must produce exactly one ledger write (the ChaincodeData record). Multiple writes in the lscc rwset violate the expected deploy semantics and are rejected.","triggerScenarios":"lsccrwset.Writes has length != 1 during DEPLOY on a channel without PrivateChannelData — e.g. custom lscc writing collection configs to lscc's namespace on a channel where private data is disabled.","commonSituations":"Passing collection-config arguments to instantiate on a channel where the capability for private channel data is disabled; modified lscc; peer with mismatched channel capabilities (V1_2 vs V1_1).","solutions":["Enable the V1_2 (or later) PrivateChannelData capability in the channel config if you need collections.","Remove collection configuration arguments from the instantiate call when the channel lacks the capability.","Run stock lscc so only a single ChaincodeData write is issued on non-capability channels.","Align all peers' channel capability versions."],"exampleFix":"// before\nrequest['collections-config'] = collectionsConfig // channel lacks PrivateChannelData capability\n// after: enable V1_2 capability in channel config, or drop collections config\nrequest = baseInstantiateRequest","handlingStrategy":"validation","validationCode":"// client-side: only send collections config when the channel has the PrivateChannelData capability\nif !channelConfig.Capabilities.PrivateChannelData() && request.CollectionsConfig != nil {\n    return fmt.Errorf(\"channel lacks PrivateChannelData capability; drop collections config or upgrade channel config\")\n}","typeGuard":"func collectionsAllowed(ac capabilities.ApplicationCapabilities) bool {\n    return ac != nil && ac.PrivateChannelData()\n}","tryCatchPattern":"if err := submitTx(envelope); err != nil {\n    if strings.Contains(err.Error(), \"can only issue a single putState upon deploy\") {\n        // either enable V1_2 channel capability or remove collections-config from instantiate\n    }\n}","preventionTips":["Enable V1_2+ application capabilities before using private data collections","Only attach collections-config when the channel supports it","Keep channel config and peer binaries version-consistent"],"tags":["hyperledger-fabric","lscc","collections","capabilities"],"backgroundTag":"missing-channel-capability","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}