dgraph-io/badger · error

cannot use NewWriteBatchAt with managedDB=false. Use NewWrit

Error message

cannot use NewWriteBatchAt with managedDB=false. Use NewWriteBatch instead

What it means

Badger's DB.NewWriteBatchAt allows the caller to set an explicit commit timestamp and is reserved for managed mode. If the DB was opened without managed transactions (WithManagedTxns(false), the default), the library panics with this message and directs you to NewWriteBatch. It is a guard against mixing timestamp-controlled writes with ordinary commit-timestamp allocation.

Source

Thrown at managed_db.go:36

// NewTransactionAt follows the same logic as DB.NewTransaction(), but uses the
// provided read timestamp.
//
// This is only useful for databases built on top of Badger (like Dgraph), and
// can be ignored by most users.
func (db *DB) NewTransactionAt(readTs uint64, update bool) *Txn {
	if !db.opt.managedTxns {
		panic("Cannot use NewTransactionAt with managedDB=false. Use NewTransaction instead.")
	}
	txn := db.newTransaction(update, true)
	txn.readTs = readTs
	return txn
}

// NewWriteBatchAt is similar to NewWriteBatch but it allows user to set the commit timestamp.
// NewWriteBatchAt is supposed to be used only in the managed mode.
func (db *DB) NewWriteBatchAt(commitTs uint64) *WriteBatch {
	if !db.opt.managedTxns {
		panic("cannot use NewWriteBatchAt with managedDB=false. Use NewWriteBatch instead")
	}

	wb := db.newWriteBatch(true)
	wb.commitTs = commitTs
	wb.txn.commitTs = commitTs
	return wb
}
func (db *DB) NewManagedWriteBatch() *WriteBatch {
	if !db.opt.managedTxns {
		panic("cannot use NewManagedWriteBatch with managedDB=false. Use NewWriteBatch instead")
	}

	wb := db.newWriteBatch(true)
	return wb
}

// CommitAt commits the transaction, following the same logic as Commit(), but
// at the given commit timestamp. This will panic if not used with managed transactions.

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Open the DB with WithManagedTxns(true) so NewWriteBatchAt is permitted.
  2. If explicit commit timestamps are unnecessary, use db.NewWriteBatch() and let Badger assign timestamps.
  3. Ensure all write paths in the app agree on managed vs non-managed mode; mixing both At and non-At APIs on one DB invites this panic.
  4. Audit callers of NewWriteBatchAt and pair each with a DB opened via a shared helper that sets the managed flag.

Example fix

// before
opts := badger.DefaultOptions(dir)
db, _ := badger.Open(opts)
wb := db.NewWriteBatchAt(42)
// after
opts := badger.DefaultOptions(dir).WithManagedTxns(true)
db, _ := badger.Open(opts)
wb := db.NewWriteBatchAt(42)
// ...or, without managed mode:
wb := db.NewWriteBatch()
Defensive patterns

Strategy: validation

Validate before calling

var wb *badger.WriteBatch
if managedMode { // the flag you passed via WithManagedTxns
    wb = db.NewWriteBatchAt(commitTs)
} else {
    wb = db.NewWriteBatch()
}

Type guard

func canUseManagedBatch(managedTxns bool) bool { return managedTxns }

Try / catch

func safeNewWriteBatchAt(db *badger.DB, commitTs uint64) (wb *badger.WriteBatch, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("NewWriteBatchAt requires managed mode: %v", r)
        }
    }()
    wb = db.NewWriteBatchAt(commitTs)
    return
}

Prevention

When it happens

Trigger: Calling db.NewWriteBatchAt(commitTs) on a DB opened without WithManagedTxns(true). The panic fires immediately at the top of NewWriteBatchAt (managed_db.go:36) before any batch is created.

Common situations: Porting bulk-loading or replication code from a managed-mode deployment to a plain application DB; copy-pasted loader code that sets commit timestamps; forgetting that the default options do not enable managed transactions.

Related errors


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