hyperledger/fabric · error

the BlockToLive in the following existing collections must n

Error message

the BlockToLive in the following existing collections must not be modified: %v

What it means

BlockToLive (the number of blocks after which private data is purged) is immutable once a collection is defined. checkForModifiedCollectionsBTL compares old and new BlockToLive values for every existing collection and rejects the upgrade transaction if any differ, listing the offending collections.

Source

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

	// modified for the existing collections.
	for _, oldCollectionConfig := range oldCollectionConfigs {

		oldCollection := oldCollectionConfig.GetStaticCollectionConfig()
		// It cannot be nil
		if oldCollection == nil {
			return policyErr(fmt.Errorf("unknown collection configuration type"))
		}

		oldCollectionName := oldCollection.GetName()
		newCollection := newCollectionsMap[oldCollectionName]
		// BlockToLive cannot be changed
		if newCollection.GetBlockToLive() != oldCollection.GetBlockToLive() {
			modifiedCollectionsBTL = append(modifiedCollectionsBTL, oldCollectionName)
		}
	}

	if len(modifiedCollectionsBTL) > 0 {
		return policyErr(fmt.Errorf("the BlockToLive in the following existing collections must not be modified: %v",
			modifiedCollectionsBTL))
	}

	return nil
}

func validateNewCollectionConfigsAgainstOld(newCollectionConfigs []*pb.CollectionConfig, oldCollectionConfigs []*pb.CollectionConfig,
) error {
	newCollectionsMap := make(map[string]*pb.StaticCollectionConfig, len(newCollectionConfigs))

	for _, newCollectionConfig := range newCollectionConfigs {
		newCollection := newCollectionConfig.GetStaticCollectionConfig()
		// Collection object itself is stored as value so that we can
		// check whether the block to live is changed -- FAB-7810
		newCollectionsMap[newCollection.GetName()] = newCollection
	}

	if err := checkForMissingCollections(newCollectionsMap, oldCollectionConfigs); err != nil {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Restore the original blockToLive value for every existing collection in the new config
  2. Keep the old collections file as the base for upgrades and only add new collections with new blockToLive values
  3. Remember retention is fixed at creation; use new collections or off-chain purge strategies if retention must change

Example fix

// before (upgrade changing existing collection)
[{"name": "coll1", "blockToLive": 1000}]
// after (keep original; new collection may set its own)
[{"name": "coll1", "blockToLive": 0}]
Defensive patterns

Strategy: validation

Validate before calling

function validateBtlUnchanged(oldConfigs, newConfigs) {
  const newMap = new Map(newConfigs.map(c => [c.name, c]));
  const changed = oldConfigs
    .filter(c => newMap.has(c.name) && newMap.get(c.name).blockToLive !== c.blockToLive)
    .map(c => c.name);
  if (changed.length) throw new Error(`blockToLive must not change for: ${changed.join(', ')}`);
}

Type guard

function btlIsImmutable(oldConfigs, newConfigs) {
  const newMap = new Map(newConfigs.map(c => [c.name, c]));
  return oldConfigs.every(c => !newMap.has(c.name) || newMap.get(c.name).blockToLive === c.blockToLive);
}

Try / catch

try {
  await contract.submitTransaction('UpgradeChaincode', ...args);
} catch (err) {
  if (String(err).includes('BlockToLive')) {
    // restore original blockToLive values and resubmit
  }
  throw err;
}

Prevention

When it happens

Trigger: Submitting a chaincode upgrade where a collection keeps its name but its blockToLive value differs from the previously committed definition, e.g. changing blockToLive from 0 to 1000.

Common situations: Editing an exported collections JSON to 'tune' retention and reusing it in an upgrade; changing blockToLive to try to purge existing private data faster; tooling that regenerates the config with different defaults (e.g. omitting a previously set blockToLive).

Related errors


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