{"record":{"id":"c5655d4ce25af7e4","repo":"dgraph-io/badger","slug":"readts-should-not-be-retrieved-for-managed-db","errorCode":null,"errorMessage":"ReadTs should not be retrieved for managed DB","messagePattern":"ReadTs should not be retrieved for managed DB","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"txn.go","lineNumber":80,"sourceCode":"\t\t//\n\t\t// WaterMarks must be 64-bit aligned for atomic package, hence we must use pointers here.\n\t\t// See https://golang.org/pkg/sync/atomic/#pkg-note-BUG.\n\t\treadMark: &y.WaterMark{Name: \"badger.PendingReads\"},\n\t\ttxnMark:  &y.WaterMark{Name: \"badger.TxnTimestamp\"},\n\t\tcloser:   z.NewCloser(2),\n\t}\n\torc.readMark.Init(orc.closer)\n\torc.txnMark.Init(orc.closer)\n\treturn orc\n}\n\nfunc (o *oracle) Stop() {\n\to.closer.SignalAndWait()\n}\n\nfunc (o *oracle) readTs() uint64 {\n\tif o.isManaged {\n\t\tpanic(\"ReadTs should not be retrieved for managed DB\")\n\t}\n\n\tvar readTs uint64\n\to.Lock()\n\treadTs = o.nextTxnTs - 1\n\to.readMark.Begin(readTs)\n\to.Unlock()\n\n\t// Wait for all txns which have no conflicts, have been assigned a commit\n\t// timestamp and are going through the write to value log and LSM tree\n\t// process. Not waiting here could mean that some txns which have been\n\t// committed would not be read.\n\ty.Check(o.txnMark.WaitForMark(context.Background(), readTs))\n\treturn readTs\n}\n\nfunc (o *oracle) nextTs() uint64 {\n\to.Lock()","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/txn.go#L62-L98","documentation":"This is a panic thrown by the oracle's readTs() when the DB is running with managed transactions enabled. In managed mode, read timestamps are supplied by the caller (via db.NewTransactionAt(ts, ...)), so fetching one internally via newTransaction is forbidden. The panic fires immediately before readTs would be computed.","triggerScenarios":"Calling db.NewTransaction(update=true) (or View/Update helpers) on a DB opened with WithManagedTxns(true); only db.NewTransactionAt(ts, update) and db.NewWriteBatchAt are valid transaction sources in managed mode.","commonSituations":"Enabling managed mode for Dgraph or custom timestamp control while leaving existing View()/Update() calls in place; library code (ORM layers, caches) that assumes standard transaction creation; toggling managedTxns in config without auditing all transaction creation sites.","solutions":["Replace db.NewTransaction/View/Update with db.NewTransactionAt(readTs, update) in managed mode, passing an appropriate read timestamp","Obtain valid timestamps via db.Sequence-free APIs like the commit callbacks or an external allocator (e.g. Badger's oracle / watermarks)","If managed mode is not needed, open the DB without WithManagedTxns(true)","Audit all transaction-creation call sites (grep for NewTransaction/View/Update) when enabling managed txns"],"exampleFix":"// before (managed DB)\ntxn := db.NewTransaction(true)\n// after (managed DB)\nreadTs := getReadTs() // from your timestamp allocator\nif readTs == 0 {\n    db.View(func(txn *badger.Txn) error { ... }) // not allowed in managed mode\n}\ntxn := db.NewTransactionAt(readTs, true)","handlingStrategy":"validation","validationCode":"if db.Opts().ManagedTxns { // must use NewTransactionAt\n    txn := db.NewTransactionAt(readTs, true)\n} else {\n    txn := db.NewTransaction(true)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Gate all transaction creation on a single helper that knows about managed mode","Pass readTs explicitly through your service layer when managed txns are enabled","Never call View/Update in managed mode"],"tags":["badger","managed-mode","read-timestamp","panic"],"backgroundTag":"managed-db-readts-forbidden","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"}