{"record":{"id":"7c7fca9daa62c39d","repo":"dgraph-io/badger","slug":"cannot-flatten-in-read-only-mode","errorCode":null,"errorMessage":"Cannot flatten in read-only mode.","messagePattern":"Cannot flatten in read-only mode\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1665,"sourceCode":"func (db *DB) startMemoryFlush() {\n\t// Start memory fluhser.\n\tif db.closers.memtable != nil {\n\t\tdb.flushChan = make(chan *memTable, db.opt.NumMemtables)\n\t\tdb.closers.memtable = z.NewCloser(1)\n\t\tgo func() {\n\t\t\tdb.flushMemtable(db.closers.memtable)\n\t\t}()\n\t}\n}\n\n// Flatten can be used to force compactions on the LSM tree so all the tables fall on the same\n// level. This ensures that all the versions of keys are colocated and not split across multiple\n// levels, which is necessary after a restore from backup. During Flatten, live compactions are\n// stopped. Ideally, no writes are going on during Flatten. Otherwise, it would create competition\n// between flattening the tree and new tables being created at level zero.\nfunc (db *DB) Flatten(workers int) error {\n\tif db.opt.ReadOnly {\n\t\tpanic(\"Cannot flatten in read-only mode.\")\n\t}\n\n\tdb.stopCompactions()\n\tdefer db.startCompactions()\n\n\tcompactAway := func(cp compactionPriority) error {\n\t\tdb.opt.Infof(\"Attempting to compact with %+v\\n\", cp)\n\t\terrCh := make(chan error, 1)\n\t\tfor i := 0; i < workers; i++ {\n\t\t\tgo func() {\n\t\t\t\terrCh <- db.lc.doCompact(175, cp)\n\t\t\t}()\n\t\t}\n\t\tvar success int\n\t\tvar rerr error\n\t\tfor i := 0; i < workers; i++ {\n\t\t\terr := <-errCh\n\t\t\tif err != nil {","sourceCodeStart":1647,"sourceCodeEnd":1683,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/db.go#L1647-L1683","documentation":"Returned by DB.Flatten when the database is in read-only mode. Flatten forces compactions across the LSM tree so all tables land on one level, and compaction writes new tables and rewrites the MANIFEST — operations that a read-only database cannot perform, hence the guard rejects the call.","triggerScenarios":"Calling db.Flatten(workers) on a DB opened with badger.ReadOnly, typically after a restore or on a read-only replica.","commonSituations":"Running flatten/defrag maintenance scripts against a read-only mounted directory; read-only disaster-recovery nodes.","solutions":["Open the DB in write mode before calling Flatten","Do Flatten on the primary/writable instance, not read-only replicas","Check db.opt.ReadOnly before calling Flatten"],"exampleFix":"// before\nopt.ReadOnly = true\ndb, _ := badger.Open(opt)\ndb.Flatten(4) // panics\n// after\nopt.ReadOnly = false // writable copy\ndb, _ := badger.Open(opt)\ndb.Flatten(4)","handlingStrategy":"validation","validationCode":"if db.opt.ReadOnly { return errors.New(\"Flatten unavailable in read-only mode\") }","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        if strings.Contains(fmt.Sprint(r), \"Cannot flatten in read-only\") { /* schedule on writable node */ }\n    }\n}()","preventionTips":["Run maintenance (Flatten/DropAll) only on writable instances","Check opt.ReadOnly before administrative operations","Recover around admin ops so config panics fail gracefully"],"tags":["badger","panic","flatten","read-only"],"backgroundTag":"unsupported-api-in-mode","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"}