hyperledger/fabric · error
the BlockToLive in an existing collection [%s] modified. Exi
Error message
the BlockToLive in an existing collection [%s] modified. Existing value [%d]
What it means
Hyperledger Fabric's lifecycle SCC rejects any attempt to modify the BlockToLive field of an already-committed private data collection. Once a collection is committed, BlockToLive is immutable; validateCollConfigsAgainstCommittedDef compares the proposed collection against the committed definition and fails when the values differ. The committed (existing) value is reported in the message.
Source
Thrown at core/chaincode/lifecycle/scc.go:952
proposedCollsMap[c.Name] = c
}
// In the new collection config package, ensure that there is one entry per old collection. Any
// number of new collections are allowed.
for _, committedCollConfig := range committedCollConfPkg.Config {
committedColl := committedCollConfig.GetStaticCollectionConfig()
// It cannot be nil
if committedColl == nil {
return errors.Errorf("unknown collection configuration type")
}
newCollection, ok := proposedCollsMap[committedColl.Name]
if !ok {
return errors.Errorf("existing collection [%s] missing in the proposed collection configuration", committedColl.Name)
}
if newCollection.BlockToLive != committedColl.BlockToLive {
return errors.Errorf("the BlockToLive in an existing collection [%s] modified. Existing value [%d]", committedColl.Name, committedColl.BlockToLive)
}
}
return nil
}
func (i *Invocation) createOpaqueStates() ([]OpaqueState, error) {
if i.ApplicationConfig == nil {
return nil, errors.Errorf("no application config for channel '%s'", i.Stub.GetChannelID())
}
orgs := i.ApplicationConfig.Organizations()
opaqueStates := make([]OpaqueState, 0, len(orgs))
for _, org := range orgs {
opaqueStates = append(opaqueStates, &ChaincodePrivateLedgerShim{
Collection: implicitcollection.NameForOrg(org.MSPID()),
Stub: i.Stub,
})
}
return opaqueStates, nilView on GitHub (pinned to 2736b63f8f)
Solutions
- Restore the BlockToLive value in the proposed config to match the committed value exactly (the existing value is printed in the error message)
- Retrieve the committed collection config (QSCC GetChaincodeCollectionConfig or QueryChaincodeDefinition) and base edits on it
- If retention must change, create a new collection with a new name (data migration) instead of mutating the existing one
- Use the peer's pre-check via CheckCommitReadiness to validate the update before committing
Example fix
// before (collections_config.json)
{"name":"coll1","blockToLive":0}
// after
{"name":"coll1","blockToLive":100000} // must equal committed value Defensive patterns
Strategy: validation
Validate before calling
// compare proposed vs committed before submitting update
committed, _ := getCommittedCollectionConfig(channel, ccName)
for _, proposed := range proposedColls {
for _, c := range committed {
if c.Name == proposed.Name && c.BlockToLive != proposed.BlockToLive {
return fmt.Errorf("BlockToLive for %s is immutable (committed=%d)", c.Name, c.BlockToLive)
}
}
} Type guard
func blockToLiveUnchanged(committed, proposed uint64) bool { return committed == proposed } Prevention
- Always fetch the committed collection config before proposing updates
- Treat BlockToLive as immutable in any config-editing tooling
- Use CheckCommitReadiness as a dry run before commit
- Create new collections for retention changes instead of editing existing ones
When it happens
Trigger: Calling _commitUpdateCollectionConfig (via chaincode Invoke on the _lifecycle SCC, e.g. through CommitChaincodeDefinition or a collection-config update transaction) with a CollectionConfigPackage where a collection with the same name has a different BlockToLive than the one on the ledger.
Common situations: Operators editing a collections_config.json to tighten or extend private data retention (e.g. changing BlockToLive from 100000 to 0 or a larger value) and redeploying; CLI tooling regenerating collection configs with different defaults; SDKs re-submitting configs where BlockToLive was changed to try to expire old private data.
Related errors
- collection-name: %s -- cannot unmarshal identity bytes into
- collection-name: %s -- collection member '%s' is not part of
- collection-name: %s -- cannot unmarshal identity bytes into
- collection-name: %s -- principal type %v is not supported
- signature policy is not an OR concatenation, NOutOf %d
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/418312d240c13fb4.
Report an issue: GitHub.