dgraph-io/dgraph · error
Transaction is too old
Error message
Transaction is too old
What it means
ErrTsTooOld is returned when a mutation arrives with a start timestamp older than the current watermark/commit state, meaning the transaction is so old its mutations can no longer be safely applied in the MVCC ordering. The transaction must be aborted and retried with a fresh timestamp.
Source
Thrown at posting/mvcc.go:62
priorityKeys []*pooledKeys
count uint64
// Get Timestamp function gets a new timestamp to store the rollup at. This makes sure that
// we are not overwriting any transaction. If there are transactions that are ongoing,
// which modify the item, rollup wouldn't affect the data, as a delta would be written
// later on
getNewTs func(bool) uint64
closer *z.Closer
}
type CachePL struct {
list *List
lastUpdate uint64
}
var (
// ErrTsTooOld is returned when a transaction is too old to be applied.
ErrTsTooOld = errors.Errorf("Transaction is too old")
// ErrInvalidKey is returned when trying to read a posting list using
// an invalid key (e.g the key to a single part of a larger multi-part list).
ErrInvalidKey = errors.Errorf("cannot read posting list using multi-part list key")
// ErrHighPriorityOp is returned when rollup is cancelled so that operations could start.
ErrHighPriorityOp = errors.New("Cancelled rollup to make way for high priority operation")
// IncrRollup is used to batch keys for rollup incrementally.
IncrRollup = &incrRollupi{
priorityKeys: make([]*pooledKeys, 2),
}
)
var MemLayerInstance *MemoryLayer
func init() {
x.AssertTrue(len(IncrRollup.priorityKeys) == 2)
for i := range IncrRollup.priorityKeys {
IncrRollup.priorityKeys[i] = &pooledKeys{View on GitHub (pinned to 759e242be6)
Solutions
- Abort the transaction and retry it with a new timestamp (client retry loop)
- Keep transactions short-lived; avoid holding mutations open during slow app logic
- Reduce write contention causing mutation queues to back up past the watermark
- Verify Alpha timestamps/watermarks are advancing normally (no stalled oracle)
Example fix
// before
err := dgraphMutate(ctx, txn) // txn started long ago
// after: retry on ErrTsTooOld
err := dgraphMutate(ctx, txn)
if errors.Is(err, posting.ErrTsTooOld) {
txn = startFreshTxn(ctx)
err = dgraphMutate(ctx, txn)
} Defensive patterns
Strategy: retry
Validate before calling
// Before mutating, ensure the txn's startTs is still valid (app-level check)
if time.Since(txnStartedAt) > maxTxnAge {
txn = restartTxn(ctx) // abort and start a fresh transaction
} Try / catch
err := runMutation(ctx, txn)
if errors.Is(err, posting.ErrTsTooOld) {
txn.Discard()
txn = dgraph.NewTxn(ctx)
err = runMutation(ctx, txn) // retry with fresh timestamp
} Prevention
- Keep transactions short; commit promptly after reads
- Never hold a write transaction across user input or long computation
- Retry on ErrTsTooOld at the client with backoff
- Monitor alpha mutation queue depth and watermark lag
When it happens
Trigger: addMutationHelper / addReverseMutationHelper receive a txn whose startTs is less than or equal to a committed watermark (e.g. after the txn was delayed in a queue, or a client held a read-only/low-timestamp transaction too long while writes committed).
Common situations: Long-lived transactions held open by a client for minutes/hours while other writers commit; heavy write load starving a queued transaction; clock/timestamp skew after cluster membership or snapshot changes.
Related errors
- while leasing txn timestamp. Id: %+v
- Adding reverse mutation helper count
- found a manifest with backup ID %s but expected %s
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/fd63f1da6e92f3de.
Report an issue: GitHub.