{"record":{"id":"e380d92b4e3f86fe","repo":"dgraph-io/badger","slug":"db-closed","errorCode":null,"errorMessage":"DB Closed","messagePattern":"DB Closed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"iterator.go","lineNumber":464,"sourceCode":"\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}\n\tfor i := 0; i < len(tables); i++ {\n\t\titers = append(iters, tables[i].sl.NewUniIterator(opt.Reverse))\n\t}","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/iterator.go#L446-L482","documentation":"ErrDBClosed is panicked by NewIterator when the underlying DB has been closed. You cannot create iterators (or transactions) against a closed database; all reads require an open DB. This can also surface when a background worker with an open iterator keeps running after db.Close().","triggerScenarios":"Calling txn.NewIterator after db.Close(); a worker goroutine creating iterators while main code closes the DB; Close being called on a DB that another component still reads from.","commonSituations":"Graceful-shutdown ordering bugs; tests closing a shared DB in cleanup while other tests still use it; singleton DB handles closed by one caller and reused by another.","solutions":["Synchronize shutdown: stop all readers/iterators (close iterators, drain workers) before db.Close()","Check db.IsClosed() before creating transactions/iterators in long-lived workers","Keep a single owner of the DB lifecycle and expose explicit Open/Close coordination"],"exampleFix":"// before\ngo worker(db)\ndb.Close()\n// after\nstop := make(chan struct{})\ngo worker(db, stop)\nclose(stop)\nwg.Wait() // worker finished and closed its iterators\ndb.Close()","handlingStrategy":"validation","validationCode":"if db.IsClosed() { return errors.New(\"db is closed; cannot iterate\") }","typeGuard":null,"tryCatchPattern":"defer func(){ if r := recover(); r != nil { if errors.Is(badger.ErrDBClosed, nil); true { log.Warn(\"iterator used after close\") } } }()\n// simpler:\ndefer func(){ if r := recover(); r != nil { log.Warnf(\"badger panic: %v\", r) } }()","preventionTips":["Order shutdown: stop workers, close iterators, then db.Close()","Guard worker loops with db.IsClosed() checks per iteration","Use context cancellation tied to DB shutdown"],"tags":["panic","db-closed","iterator","shutdown-order"],"backgroundTag":"db-closed","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"}