hyperledger/fabric · error
LSCC can only issue a single putState upon deploy
Error message
LSCC can only issue a single putState upon deploy
What it means
On the DEPLOY path, when private channel data (collections) is not enabled, lscc's write-set must contain exactly one write — the ChaincodeData putState. Any additional (or zero) writes means the transaction does not conform to the expected deploy semantics and is rejected.
Source
Thrown at core/handlers/validation/builtin/v13/lscc_validation_logic.go:512
/* 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))
}
/****************************************************************************/
/* security check 2 - validation of rwset (and of collections if enabled) */
/****************************************************************************/
if ac.PrivateChannelData() {
// 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 deploy"))
}
}
/*****************************************************/
/* security check 3 - check the instantiation policy */
/*****************************************************/
pol := cdRWSet.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 implementation so deploy writes exactly one ChaincodeData record.
- If you modified lscc, move any extra writes out of the deploy path.
- For hand-built test rwsets, ensure exactly one write on the lscc namespace with a valid ChaincodeData value.
- Enable/align private-channel-data handling consistently across peers if collections are intended.
Example fix
// before (custom lscc) stub.PutState(ccname, cdBytes) stub.PutState(ccname+"~meta", metaBytes) // extra write // after stub.PutState(ccname, cdBytes) // single write only
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight on custom lscc rwsets
lsccWrites := countWrites(nsRwSet["lscc"])
if lsccWrites != 1 {
return fmt.Errorf("lscc deploy must write exactly 1 key, got %d", lsccWrites)
} Type guard
func isSingleWrite(ns *rwset.NsRwSet) bool { return ns != nil && len(ns.KvRwSet.Writes) == 1 } Try / catch
if err != nil && strings.Contains(err.Error(), "single putState upon deploy") { /* strip extra writes from the deploy rwset or use stock lscc */ } Prevention
- Run stock lscc on all peers
- Review any lscc patches for extra writes
- Validate fabricated test rwsets against the one-write contract
When it happens
Trigger: An lscc deploy whose lscc-namespaced rwset contains 0 or 2+ writes — e.g. a modified lscc emitting extra puts, or a hand-crafted rwset for testing.
Common situations: Patched/custom lscc implementations writing extra keys on deploy, integration tests fabricating lscc rwsets manually, mixing deploy-time collection configuration with a non-private-data channel code path.
Related errors
- No read write set for lscc was found
- LSCC must issue at least one single putState upon deploy/upg
- LSCC invocation is attempting to write to namespace %s
- LSCC can only issue a single putState upon upgrade
- LSCC can only issue one or two putState upon deploy
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/753fa326bb097865.
Report an issue: GitHub.