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
- Retry the entire transaction in a loop on ErrConflict with a fresh transaction
- Shorten transactions: read late, write promptly, commit fast
- Avoid reading keys you don't mutate (shrinks the conflict fingerprint)
- 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
- Always wrap read-modify-write workloads in a conflict-retry loop with backoff
- Keep transactions short and read only the keys you write
- Avoid holding transactions open across network calls
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.