hyperledger/fabric · error

Private data is forbidden to be used in instantiate

Error message

Private data is forbidden to be used in instantiate

What it means

During simulation, if the transaction produced private-data writes (PvtSimulationResults) and the target chaincode is lscc, the endorser rejects the proposal: collection configuration in instantiate could not be stored outside LSCC at the time, so private data in instantiate is forbidden.

Source

Thrown at core/endorser/endorser.go:212

		return res, nil, ccevent, nil, nil
	}

	// Note, this is a little goofy, as if there is private data, Done() gets called
	// early, so this is invoked multiple times, but that is how the code worked before
	// this change, so, should be safe.  Long term, let's move the Done up to the create.
	defer txParams.TXSimulator.Done()

	simResult, err := txParams.TXSimulator.GetTxSimulationResults()
	if err != nil {
		e.Metrics.SimulationFailure.With(meterLabels...).Add(1)
		return nil, nil, nil, nil, err
	}

	if simResult.PvtSimulationResults != nil {
		if chaincodeName == "lscc" {
			// TODO: remove once we can store collection configuration outside of LSCC
			e.Metrics.SimulationFailure.With(meterLabels...).Add(1)
			return nil, nil, nil, nil, errors.New("Private data is forbidden to be used in instantiate")
		}
		pvtDataWithConfig, err := AssemblePvtRWSet(txParams.ChannelID, simResult.PvtSimulationResults, txParams.TXSimulator, e.Support.GetDeployedCCInfoProvider())
		// To read collection config need to read collection updates before
		// releasing the lock, hence txParams.TXSimulator.Done()  moved down here
		txParams.TXSimulator.Done()

		if err != nil {
			e.Metrics.SimulationFailure.With(meterLabels...).Add(1)
			return nil, nil, nil, nil, errors.WithMessage(err, "failed to obtain collections config")
		}
		endorsedAt, err := e.Support.GetLedgerHeight(txParams.ChannelID)
		if err != nil {
			e.Metrics.SimulationFailure.With(meterLabels...).Add(1)
			return nil, nil, nil, nil, errors.WithMessage(err, fmt.Sprintf("failed to obtain ledger height for channel '%s'", txParams.ChannelID))
		}
		// Add ledger height at which transaction was endorsed,
		// `endorsedAt` is obtained from the block storage and at times this could be 'endorsement Height + 1'.
		// However, since we use this height only to select the configuration (3rd parameter in distributePrivateData) and

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Use the new lifecycle (_lifecycle) approve/commit flow where collection configuration is passed to commit
  2. Remove private-data/collection definitions from the legacy instantiate path
  3. Update tooling so collections are configured outside lscc

Example fix

// before
peer chaincode instantiate -C mychannel -n mycc --collections-config collections.json ...
// after
peer lifecycle chaincode approveformyorg ... --collections-config collections.json
peer lifecycle chaincode commit -C mychannel -n mycc ...
Defensive patterns

Strategy: validation

Validate before calling

if chaincodeName == "lscc" && simResult.PvtSimulationResults != nil {
    return errors.New("use _lifecycle commit with collections config instead")
}

Prevention

When it happens

Trigger: Calling lscc instantiate/upgrade with a chaincode that defines collections, causing the simulation to write private data (collection config updates) which lscc proposals may not carry.

Common situations: Deploying a chaincode with collection-config via the legacy lscc path; old deployment scripts combining private collections with instantiate; missing the separate collection-config handling introduced later.

Understand the failure class

Related errors


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