{"record":{"id":"ef04fda9fdecd485","repo":"dgraph-io/badger","slug":"attempting-to-drop-data-in-read-only-mode","errorCode":null,"errorMessage":"Attempting to drop data in read-only mode.","messagePattern":"Attempting to drop data in read-only mode\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1756,"sourceCode":"\t}\n\n\t// Make all pending writes finish. The following will also close writeCh.\n\tdb.closers.writes.SignalAndWait()\n\tdb.opt.Infof(\"Writes flushed. Stopping compactions now...\")\n\treturn nil\n}\n\nfunc (db *DB) unblockWrite() {\n\tdb.closers.writes = z.NewCloser(1)\n\tgo db.doWrites(db.closers.writes)\n\n\t// Resume writes.\n\tdb.blockWrites.Store(0)\n}\n\nfunc (db *DB) prepareToDrop() (func(), error) {\n\tif db.opt.ReadOnly {\n\t\tpanic(\"Attempting to drop data in read-only mode.\")\n\t}\n\t// In order prepare for drop, we need to block the incoming writes and\n\t// write it to db. Then, flush all the pending memtable. So that, we\n\t// don't miss any entries.\n\tif err := db.blockWrite(); err != nil {\n\t\treturn func() {}, err\n\t}\n\treqs := make([]*request, 0, 10)\n\tfor {\n\t\tselect {\n\t\tcase r := <-db.writeCh:\n\t\t\treqs = append(reqs, r)\n\t\tdefault:\n\t\t\tif err := db.writeRequests(reqs); err != nil {\n\t\t\t\tdb.opt.Errorf(\"writeRequests: %v\", err)\n\t\t\t}\n\t\t\tdb.stopMemoryFlush()\n\t\t\treturn func() {","sourceCodeStart":1738,"sourceCodeEnd":1774,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/db.go#L1738-L1774","documentation":"This is a deliberate panic thrown by badger's prepareToDrop() when the database was opened with ReadOnly: true and a drop operation (DropAll/DropNamespace) is attempted. Dropping data requires flushing memtables and writing new state to disk, which is impossible in read-only mode, so the library fails fast instead of silently corrupting or refusing later.","triggerScenarios":"Calling db.DropAll() or db.DropNamespace() (or any API that funnels into db.prepareToDrop()) on a DB opened via badger.Open with opt.ReadOnly = true.","commonSituations":"A maintenance/admin script reusing a shared Open() helper that hardcodes ReadOnly: true; copying a DB-open config from a replica/backup-reader and then running destructive cleanup; CI jobs that mount the DB directory read-only and pass ReadOnly to match, then still try to drop data.","solutions":["Open the DB without ReadOnly: true before calling DropAll/DropNamespace","Skip the drop for read-only handles (e.g. if opt.ReadOnly, return early instead of dropping)","Use a separate writable connection for administrative operations like drops"],"exampleFix":"// before\ndb, err := badger.Open(badger.DefaultOptions(dir).WithReadOnly(true))\nerr = db.DropAll()\n\n// after\ndb, err := badger.Open(badger.DefaultOptions(dir)) // writable\nerr = db.DropAll()","handlingStrategy":"validation","validationCode":"if db.Opts().ReadOnly { /* skip drop or open writable DB */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hardcode ReadOnly: true in shared Open helpers used by admin code paths","Route destructive operations (DropAll/DropNamespace) through a dedicated writable connection","Panic in Go is not recoverable via error return — use recover() only at a top-level boundary if you must tolerate it"],"tags":["badger","read-only","panic","drop-all"],"backgroundTag":"read-only-mode-violation","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"}