{"record":{"id":"2a3a5e0be623798b","repo":"hyperledger/fabric","slug":"validateandprepare-method-should-have-been-calle","errorCode":null,"errorMessage":"validateAndPrepare() method should have been called before calling commit()","messagePattern":"validateAndPrepare\\(\\) method should have been called before calling commit\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/kvledger/txmgmt/txmgr/lockbased_txmgr.go","lineNumber":536,"sourceCode":"\tdefer txmgr.oldBlockCommit.Unlock()\n\tlogger.Debug(\"lock acquired on oldBlockCommit for committing regular updates to state database\")\n\n\t// When using the purge manager for the first block commit after peer start, the asynchronous function\n\t// 'PrepareForExpiringKeys' is invoked in-line. However, for the subsequent blocks commits, this function is invoked\n\t// in advance for the next block\n\tif !txmgr.pvtdataPurgeMgr.usedOnce {\n\t\ttxmgr.pvtdataPurgeMgr.PrepareForExpiringKeys(txmgr.currentUpdates.blockNum())\n\t\ttxmgr.pvtdataPurgeMgr.usedOnce = true\n\t}\n\tdefer func() {\n\t\ttxmgr.pvtdataPurgeMgr.PrepareForExpiringKeys(txmgr.currentUpdates.blockNum() + 1)\n\t\tlogger.Debugf(\"launched the background routine for preparing keys to purge with the next block\")\n\t\ttxmgr.reset()\n\t}()\n\n\tlogger.Debugf(\"Committing updates to state database\")\n\tif txmgr.currentUpdates == nil {\n\t\tpanic(\"validateAndPrepare() method should have been called before calling commit()\")\n\t}\n\n\tif err := txmgr.pvtdataPurgeMgr.UpdateExpiryInfo(\n\t\ttxmgr.currentUpdates.batch.PvtUpdates, txmgr.currentUpdates.batch.HashUpdates,\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tif err := txmgr.pvtdataPurgeMgr.AddExpiredEntriesToUpdateBatch(\n\t\ttxmgr.currentUpdates.batch.PvtUpdates, txmgr.currentUpdates.batch.HashUpdates,\n\t); err != nil {\n\t\treturn err\n\t}\n\n\tcommitHeight := version.NewHeight(txmgr.currentUpdates.blockNum(), txmgr.currentUpdates.maxTxNumber())\n\ttxmgr.commitRWLock.Lock()\n\tlogger.Debugf(\"Write lock acquired for committing updates to state database\")\n\tif err := txmgr.db.ApplyPrivacyAwareUpdates(txmgr.currentUpdates.batch, commitHeight); err != nil {","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/kvledger/txmgmt/txmgr/lockbased_txmgr.go#L518-L554","documentation":"LockBasedTxMgr.Commit enforces the lifecycle validateAndPrepare() -> commit(). currentUpdates is only set by validateAndPrepare; committing without it means the batch was never staged, so the manager panics to prevent committing an undefined set of updates.","triggerScenarios":"Calling Commit() on a LockBasedTxMgr without a preceding validateAndPrepare() call in the same block-processing cycle — e.g. custom code or tests invoking Commit directly, or a code path that resets the txmgr between prepare and commit.","commonSituations":"Test code simulating block commit that skips the validate step; custom block-processing integrations calling txmgr.Commit out of order; race conditions where reset() (scheduled in the background routine) runs before Commit.","solutions":["Always call validateAndPrepare(block) before Commit() in the same block-processing sequence.","If writing a test, replicate the full commit flow: txmgr.ValidateAndPrepareKVReads/validateAndPrepare then Commit.","Check that no code path calls txmgr.reset() between validateAndPrepare and Commit.","If using higher-level APIs (CommitLostBlock, ledger commit path), ensure the internal sequence is intact rather than calling Commit directly."],"exampleFix":"// before\ntxmgr.Commit()\n// after\ntxmgr.validateAndPrepare(block) // stages currentUpdates\ntxmgr.Commit()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Commit panics on lifecycle violation; wrap block-commit entry point\nfunc safeCommit(txmgr *txmgr.LockBasedTxMgr, block *common.Block) (err error) {\n  defer func() {\n    if r := recover(); r != nil {\n      err = fmt.Errorf(\"txmgr commit panic: %v\", r)\n    }\n  }()\n  txmgr.Commit()\n  return nil\n}","preventionTips":["Enforce the validateAndPrepare() -> Commit() ordering in all block-processing code","Never call txmgr.reset() between validateAndPrepare and Commit","Write tests that exercise the full prepare/commit cycle, not Commit in isolation","Prefer higher-level ledger commit APIs over direct txmgr.Commit calls"],"tags":["txmgr","lifecycle-violation","panic","commit"],"backgroundTag":"method-called-out-of-order","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"}