dgraph-io/badger · error

CommitTs cannot be zero. Please use commitAt instead

Error message

CommitTs cannot be zero. Please use commitAt instead

What it means

In managed-database mode, transactions using write-batching with transaction markers (keepTogether=true) require a nonzero commit timestamp set via txn.CommitAt. This error is returned by commitPrecheck when txn.Commit() is called on a managed transaction whose commitTs is zero, meaning the user called Commit instead of CommitAt. Managed mode gives the caller full control of timestamps, so a zero timestamp is invalid.

Source

Thrown at txn.go:626

func (txn *Txn) commitPrecheck() error {
	if txn.discarded {
		return errors.New("Trying to commit a discarded txn")
	}
	keepTogether := true
	for _, e := range txn.pendingWrites {
		if e.version != 0 {
			keepTogether = false
		}
	}

	// If keepTogether is True, it implies transaction markers will be added.
	// In that case, commitTs should not be never be zero. This might happen if
	// someone uses txn.Commit instead of txn.CommitAt in managed mode.  This
	// should happen only in managed mode. In normal mode, keepTogether will
	// always be true.
	if keepTogether && txn.db.opt.managedTxns && txn.commitTs == 0 {
		return errors.New("CommitTs cannot be zero. Please use commitAt instead")
	}
	return nil
}

// Commit commits the transaction, following these steps:
//
// 1. If there are no writes, return immediately.
//
// 2. Check if read rows were updated since txn started. If so, return ErrConflict.
//
// 3. If no conflict, generate a commit timestamp and update written rows' commit ts.
//
// 4. Batch up all writes, write them to value log and LSM tree.
//
// 5. If callback is provided, Badger will return immediately after checking
// for conflicts. Writes to the database will happen in the background.  If
// there is a conflict, an error will be returned and the callback will not
// run. If there are no conflicts, the callback will be called in the

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Use txn.CommitAt(ts, nil) instead of txn.Commit() in managed mode, passing a valid nonzero timestamp
  2. Or set txn.SetCommitTs(ts) before calling CommitWith
  3. Verify opt.managedTxns is intentional; if you don't need timestamp control, disable managed mode and use Commit() normally
  4. Ensure the commit timestamp comes from a monotonic source (e.g. db.NewWriteBatchAt or oracle-provided ts), not an uninitialized zero value

Example fix

// before (managed mode)
txn := db.NewTransactionAt(readTs, true)
txn.Set(key, val)
txn.Commit()
// after
txn := db.NewTransactionAt(readTs, true)
txn.Set(key, val)
commitTs := getNextTs() // nonzero, monotonic
txn.CommitAt(commitTs, nil)
Defensive patterns

Strategy: validation

Validate before calling

// in managed mode, before committing:
if commitTs == 0 {
    return errors.New("managed txns require a nonzero commit timestamp; use CommitAt")
}
txn.CommitAt(commitTs, nil)

Prevention

When it happens

Trigger: Calling db.Opts.WithManagedTxns(true) and then using txn.Commit() or CommitWith() without first setting a commit timestamp; the correct call is txn.CommitAt(commitTs, callback) or using db.NewTransactionAt with SetCommitTs. Only occurs when managedTxns is enabled and pendingWrites have version==0 (keepTogether true).

Common situations: Migrating code from normal to managed mode without switching Commit -> CommitAt; Dgraph-style layered usage where the caller forgot to propagate the commit timestamp; forgetting to call txn.SetCommitTs before CommitWith.

Related errors


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