dgraph-io/badger · warning

ErrConflict

ErrConflict

Error message

Transaction Conflict. Please retry

What it means

ErrConflict is returned from Txn.Commit (commitAndSend; detected via orc.newCommitTs at txn.go:532) when another committed transaction modified keys this transaction read — Badger's optimistic concurrency control. The transaction was NOT committed; the caller must retry with a fresh one.

Source

Thrown at errors.go:31

const (
	// ValueThresholdLimit is the maximum permissible value of opt.ValueThreshold.
	ValueThresholdLimit = math.MaxUint16 - 16 + 1
)

var (
	// ErrValueLogSize is returned when opt.ValueLogFileSize option is not within the valid
	// range.
	ErrValueLogSize = stderrors.New("Invalid ValueLogFileSize, must be in range [1MB, 2GB)")

	// ErrKeyNotFound is returned when key isn't found on a txn.Get.
	ErrKeyNotFound = stderrors.New("Key not found")

	// ErrTxnTooBig is returned if too many writes are fit into a single transaction.
	ErrTxnTooBig = stderrors.New("Txn is too big to fit into one request")

	// ErrConflict is returned when a transaction conflicts with another transaction. This can
	// happen if the read rows had been updated concurrently by another transaction.
	ErrConflict = stderrors.New("Transaction Conflict. Please retry")

	// ErrReadOnlyTxn is returned if an update function is called on a read-only transaction.
	ErrReadOnlyTxn = stderrors.New("No sets or deletes are allowed in a read-only transaction")

	// ErrDiscardedTxn is returned if a previously discarded transaction is reused.
	ErrDiscardedTxn = stderrors.New("This transaction has been discarded. Create a new one")

	// ErrEmptyKey is returned if an empty key is passed on an update function.
	ErrEmptyKey = stderrors.New("Key cannot be empty")

	// ErrInvalidKey is returned if the key has a special !badger! prefix,
	// reserved for internal usage.
	ErrInvalidKey = stderrors.New("Key is using a reserved !badger! prefix")

	// ErrBannedKey is returned if the read/write key belongs to any banned namespace.
	ErrBannedKey = stderrors.New("Key is using the banned prefix")

	// ErrThresholdZero is returned if threshold is set to zero, and value log GC is called.

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Retry the entire transaction in a loop on ErrConflict with a fresh transaction
  2. Shorten transactions: read late, write promptly, commit fast
  3. Avoid reading keys you don't mutate (shrinks the conflict fingerprint)
  4. Use external locking or managed mode with explicit timestamps for hot keys

Example fix

// before
err := db.Update(func(txn *Txn) error { ...read-modify-write... }) // fails once on conflict
// after
for {
    err := db.Update(func(txn *Txn) error { ...read-modify-write... })
    if err == badger.ErrConflict { continue }
    return err
}
Defensive patterns

Strategy: retry

Try / catch

for attempt := 0; attempt < maxRetries; attempt++ {
    err := db.Update(func(txn *Txn) error { ... })
    if errors.Is(err, badger.ErrConflict) {
        time.Sleep(backoff(attempt))
        continue
    }
    return err
}
return ErrTooManyConflicts

Prevention

When it happens

Trigger: Two concurrent read-write transactions read the same key and one commits first; CommitAt in managed mode on a conflicting txn (txn_test.go:818); long read-modify-write transactions racing frequent writers.

Common situations: Counters/rate-limiters under concurrency, optimistic loops without retry, batch jobs colliding with live traffic.

Related errors


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