{"record":{"id":"0b622c506e6d11a4","repo":"hyperledger/fabric","slug":"chaincode-cache-at-sequence-d-but-current-sequenc","errorCode":null,"errorMessage":"chaincode cache at sequence %d but current sequence is %d, chaincode definition for '%s' changed during invoke","messagePattern":"chaincode cache at sequence (.+?) but current sequence is (.+?), chaincode definition for '(.+?)' changed during invoke","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/chaincode/lifecycle/endorsement_info.go","lineNumber":85,"sourceCode":"\t\treturn nil, false, errors.WithMessagef(err, \"could not get current sequence for chaincode '%s' on channel '%s'\", chaincodeName, channelID)\n\t}\n\n\t// Committed sequences begin at 1\n\tif currentSequence == 0 {\n\t\treturn nil, false, nil\n\t}\n\n\tchaincodeInfo, err := cei.Cache.ChaincodeInfo(channelID, chaincodeName)\n\tif err != nil {\n\t\treturn nil, false, errors.WithMessage(err, \"could not get approved chaincode info from cache\")\n\t}\n\n\tif chaincodeInfo.Definition.Sequence != currentSequence {\n\t\t// TODO this is a transient error which indicates that this query executor is executing against a chaincode\n\t\t// whose definition has already changed (the cache may be ahead of the committed state, but never behind).  In this\n\t\t// case, we should simply abort the tx, and re-acquire a query executor and re-execute.  There is no reason this\n\t\t// error needs to be returned to the client.\n\t\treturn nil, false, errors.Errorf(\"chaincode cache at sequence %d but current sequence is %d, chaincode definition for '%s' changed during invoke\", chaincodeInfo.Definition.Sequence, currentSequence, chaincodeName)\n\t}\n\n\tif !chaincodeInfo.Approved {\n\t\treturn nil, false, errors.Errorf(\"chaincode definition for '%s' at sequence %d on channel '%s' has not yet been approved by this org\", chaincodeName, currentSequence, channelID)\n\t}\n\n\tif chaincodeInfo.InstallInfo == nil {\n\t\tif cei.UserRunsCC {\n\t\t\tchaincodeInfo.InstallInfo = &ChaincodeInstallInfo{\n\t\t\t\tPackageID: chaincodeName + \":\" + chaincodeInfo.Definition.EndorsementInfo.Version,\n\t\t\t}\n\t\t\treturn chaincodeInfo, true, nil\n\t\t}\n\t\treturn nil, false, errors.Errorf(\"chaincode definition for '%s' exists, but chaincode is not installed\", chaincodeName)\n\t}\n\n\treturn chaincodeInfo, true, nil\n}","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/chaincode/lifecycle/endorsement_info.go#L67-L103","documentation":"CachedChaincodeInfo detects that the chaincode definition sequence in the lifecycle cache differs from the sequence read from the current query executor's committed state. Per the source comment, the cache can be ahead of committed state but never behind, so this transient condition means the chaincode definition changed while the invoke was executing; the transaction must be aborted and re-executed with a fresh query executor.","triggerScenarios":"A chaincode definition commit (sequence bump) lands on the channel while an invocation of that chaincode is mid-flight using a query executor snapshot from the older sequence; ChaincodeEndorsementInfo then calls CachedChaincodeInfo and the sequences mismatch.","commonSituations":"Committing a chaincode upgrade concurrently with live traffic; long-running invocations overlapping an upgrade window; multiple orgs committing definitions while endorsements are in progress.","solutions":["Retry the transaction — the error is explicitly designed to be transient and retried with a fresh query executor.","Coordinate chaincode upgrades with deployment downtime or retry logic in the client SDK.","Enable client-side retry-on-error for transient lifecycle errors during upgrade windows."],"exampleFix":"// before: single attempt\nresp := await contract.submitTransaction('tx', arg)\n// after: retry transient 'changed during invoke'\nfor i := 0; i < 3; i++ {\n  resp, err = submit(); if err == nil || !strings.Contains(err.Error(), \"changed during invoke\") { break }\n  time.Sleep(time.Second)\n}","handlingStrategy":"retry","validationCode":"// Go: read current sequence before submit and retry on mismatch\ndefn, _ := client.Lifecycle().QueryChaincodeDefinition(ccName, channel)\n// submit; if 'changed during invoke' error, re-read defn.Sequence and retry","typeGuard":null,"tryCatchPattern":"err := contract.SubmitTransaction(\"tx\", arg)\nif err != nil && strings.Contains(err.Error(), \"changed during invoke\") {\n  // transient: rebuild tx with fresh query executor and retry\n  time.Sleep(500 * time.Millisecond); return submitWithRetry()\n}","preventionTips":["Schedule chaincode upgrades in quiet windows.","Wrap submits in retry logic keyed on this transient message.","Keep invocations short so they don't straddle a commit.","Monitor lifecycle commit events and drain traffic during upgrades."],"tags":["hyperledger-fabric","chaincode-lifecycle","sequence-mismatch","transient","retry"],"backgroundTag":"chaincode-definition-changed-during-invoke","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"}