dgraph-io/badger · error

Unable to do incremental writes because MemTable has data

Error message

Unable to do incremental writes because MemTable has data

What it means

PrepareIncremental requires the DB to have all data flushed out of memtables so that the incremental StreamWriter can exclusively own the LSM levels. If any memtable still holds data, the library returns this error instead of producing a corrupted incremental snapshot.

Source

Thrown at stream_writer.go:100

	// Before we start writing, we'll stop the compactions because no one else should be writing to
	// the same level as the stream writer is writing to.
	f, err := sw.db.prepareToDrop()
	if err != nil {
		sw.done = func() { once.Do(f) }
		return err
	}
	sw.db.stopCompactions()
	done := func() {
		sw.db.startCompactions()
		f()
	}
	sw.done = func() { once.Do(done) }

	mts, decr := sw.db.getMemTables()
	defer decr()
	for _, m := range mts {
		if !m.sl.Empty() {
			return fmt.Errorf("Unable to do incremental writes because MemTable has data")
		}
	}

	isEmptyDB := true
	for _, level := range sw.db.Levels() {
		if level.NumTables > 0 {
			sw.prevLevel = level.Level
			isEmptyDB = false
			break
		}
	}
	if isEmptyDB {
		// If DB is empty, we should allow doing incremental stream write.
		return nil
	}
	if sw.prevLevel == 0 {
		// It seems that data is present in all levels from Lmax to L0. If we call flatten
		// on the tree, all the data will go to Lmax. All the levels above will be empty

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Flush memtables first: call db.Flush() (or db.Close + reopen) so all memtables are empty, then call PrepareIncremental()
  2. Pause/stop all writers and wait for pending transactions to commit before preparing the incremental writer
  3. If the DB is idle and data still lingers, tune WithMemTableSize lower or trigger a manual flush via db.Flush()

Example fix

// before
sw := db.NewStreamWriter()
err := sw.PrepareIncremental() // fails: memtable has data
// after
sw := db.NewStreamWriter()
if err := db.Flush(); err != nil { /* handle */ } // ensure memtables are empty
err := sw.PrepareIncremental()
Defensive patterns

Strategy: validation

Validate before calling

// PrepareIncremental is only valid when all memtables are empty.
if err := db.Flush(); err != nil { return err } // ensures memtable data is flushed to LSM
if db.IsClosed() { return errors.New("db closed") }
err := sw.PrepareIncremental()
if err != nil && strings.Contains(err.Error(), "MemTable has data") {
    // quiesce writes and flush, then retry
}

Prevention

When it happens

Trigger: Calling StreamWriter.PrepareIncremental() while memtables are non-empty — i.e., writes/commits happened shortly before and were not flushed, or flushes were blocked.

Common situations: Running incremental stream backups on a live, write-heavy DB without forcing a flush first; integrating PrepareIncremental into a pipeline that does not quiesce writes.

Related errors


AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05). Data as JSON: /api/errors/3a5d5410d9526596. Report an issue: GitHub.