etcd-io/etcd · error
unexpected revision in delete
Error message
unexpected revision in delete
What it means
OpDelete rejects WithRev(n). WithRev pins a range read to a historical revision (MVCC snapshot); deletes always operate on the latest state, so a revision-pinned delete is meaningless. applyOpts records ret.rev, and the post-condition switch panics with "unexpected revision in delete" when it is non-zero.
Source
Thrown at client/v3/op.go:281
ret.applyOpts(opts)
return ret
}
// OpDelete returns "delete" operation based on given key and operation options.
func OpDelete(key string, opts ...OpOption) Op {
// WithPrefix and WithFromKey are not supported together
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
}
ret := Op{t: tDeleteRange, key: []byte(key)}
ret.applyOpts(opts)
switch {
case ret.leaseID != 0:
panic("unexpected lease in delete")
case ret.limit != 0:
panic("unexpected limit in delete")
case ret.rev != 0:
panic("unexpected revision in delete")
case ret.sort != nil:
panic("unexpected sort in delete")
case ret.serializable:
panic("unexpected serializable in delete")
case ret.countOnly:
panic("unexpected countOnly in delete")
case ret.minModRev != 0, ret.maxModRev != 0:
panic("unexpected mod revision filter in delete")
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
panic("unexpected create revision filter in delete")
case ret.filterDelete, ret.filterPut:
panic("unexpected filter in delete")
case ret.createdNotify:
panic("unexpected createdNotify in delete")
}
return ret
}
View on GitHub (pinned to f744d457f4)
Solutions
- Remove WithRev from Delete; if you need conditional deletion, use cli.Txn(...).If(clientv3.Compare(clientv3.CompareModified(k), "=", rev)).Then(clientv3.OpDelete(k)).
- Never share option slices between the Get and the Delete of a read-modify-write cycle.
Example fix
// before
_, err := cli.Delete(ctx, k, clientv3.WithRev(seenRev)) // panics
// after — conditional delete on the observed revision
resp, err := cli.Txn(ctx).
If(clientv3.Compare(clientv3.CompareModified(k), "=", seenRev)).
Then(clientv3.OpDelete(k)).
Commit() Defensive patterns
Strategy: validation
Prevention
- WithRev is a read-only (historical snapshot) option; it never applies to writes.
- For revision-conditional deletes, use a Txn If with CompareModified/CompareCreated.
- Never reuse a snapshot Get's option slice for the corresponding write.
When it happens
Trigger: clientv3.OpDelete(k, clientv3.WithRev(1234)); reusing an option slice that was built for a consistent historical Get; 'delete if it looked like X at revision R' attempts — that belongs to a Txn If-guard, not WithRev.
Common situations: Read-modify-delete flows where the Get used WithRev and the options object is reused; code imported from a watch-replay routine that pins revisions; conflating Txn If CompareModified with WithRev.
Related errors
- unexpected revision in put
- unexpected lease in delete
- unexpected limit in delete
- unexpected sort in delete
- unexpected serializable in delete
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/d5b886deda5a2591.
Report an issue: GitHub.