{"record":{"id":"046dcb25b728b99b","repo":"dgraph-io/badger","slug":"this-transaction-has-been-discarded-create-a-new","errorCode":null,"errorMessage":"This transaction has been discarded. Create a new one","messagePattern":"This transaction has been discarded\\. Create a new one","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"iterator.go","lineNumber":461,"sourceCode":"\t// iterators created by the stream interface\n\tThreadId int\n\n\tAlloc *z.Allocator\n}\n\n// NewIterator returns a new iterator. Depending upon the options, either only keys, or both\n// key-value pairs would be fetched. The keys are returned in lexicographically sorted order.\n// Using prefetch is recommended if you're doing a long running iteration, for performance.\n//\n// Multiple Iterators:\n// For a read-only txn, multiple iterators can be running simultaneously. However, for a read-write\n// txn, iterators have the nuance of being a snapshot of the writes for the transaction at the time\n// iterator was created. If writes are performed after an iterator is created, then that iterator\n// will not be able to see those writes. Only writes performed before an iterator was created can be\n// viewed.\nfunc (txn *Txn) NewIterator(opt IteratorOptions) *Iterator {\n\tif txn.discarded {\n\t\tpanic(ErrDiscardedTxn)\n\t}\n\tif txn.db.IsClosed() {\n\t\tpanic(ErrDBClosed)\n\t}\n\n\ty.NumIteratorsCreatedAdd(txn.db.opt.MetricsEnabled, 1)\n\n\t// Keep track of the number of active iterators.\n\ttxn.numIterators.Add(1)\n\n\t// TODO: If Prefix is set, only pick those memtables which have keys with the prefix.\n\ttables, decr := txn.db.getMemTables()\n\tdefer decr()\n\ttxn.db.vlog.incrIteratorCount()\n\tvar iters []y.Iterator\n\tif itr := txn.newPendingWritesIterator(opt.Reverse); itr != nil {\n\t\titers = append(iters, itr)\n\t}","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/iterator.go#L443-L479","documentation":"ErrDiscardedTxn is panicked when operating on a Txn after Discard() (explicit, or via defer after Commit). NewIterator refuses to build a snapshot from a discarded txn, and since iterators like Rewind/Seek/Next call setIdx against the txn state, using an iterator after its txn was discarded surfaces this panic. Each transaction is single-use; you must create a new one after discarding.","triggerScenarios":"Calling txn.NewIterator after txn.Discard() or txn.Commit(); holding an Iterator and calling Rewind/Seek/Next after the enclosing transaction was committed/discarded; reusing a txn returned by db.NewTransaction across an err return path that already discarded it.","commonSituations":"Long-lived iterators in loops that outlive the txn; defer txn.Discard() executed early via function return while a goroutine still iterates; err paths that discard then retry with the same txn object.","solutions":["Create the iterator inside the same scope/lifetime as the transaction and finish iteration before Commit/Discard","If the txn was discarded, call db.NewTransaction again and build a fresh iterator","Fix goroutine ownership: pass the iterator and txn together and never use them after the owning function returns"],"exampleFix":"// before\ntxn := db.NewTransaction(false)\ndefer txn.Discard()\nit := txn.NewIterator(opts)\ngo func(){ for it.Rewind(); it.Valid(); it.Next() {} }() // races discard\n// after\ntxn := db.NewTransaction(false)\nit := txn.NewIterator(opts)\nfor it.Rewind(); it.Valid(); it.Next() {}\nit.Close()\ntxn.Discard()","handlingStrategy":"try-catch","validationCode":"if txn == nil || txn.Discarded() { txn = db.NewTransaction(false) }","typeGuard":null,"tryCatchPattern":"func safeIter(db *badger.DB, opts badger.IteratorOptions) {\n    defer func(){ recover() }() // iterator APIs panic on discarded txn\n    txn := db.NewTransaction(false)\n    defer txn.Discard()\n    it := txn.NewIterator(opts)\n    defer it.Close()\n    for it.Rewind(); it.Valid(); it.Next() {}\n}","preventionTips":["Scope iterators to the transaction's lifetime; close iterator before Discard/Commit","Never share txn or Iterator across goroutines","Recreate the transaction after any Discard"],"tags":["panic","discarded-transaction","iterator","lifecycle"],"backgroundTag":"transaction-discarded","analyzedSha":"2a001d466f6b71a917319a1db41f99860e16e269","analyzedAt":"2026-09-05T13:00:02.264Z","contentChangedAt":"2026-09-05T13:00:02.264Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}