etcd-io/etcd · error

unexpected create revision filter in delete

Error message

unexpected create revision filter in delete

What it means

OpDelete rejects WithMinCreateRev / WithMaxCreateRev. Create-revision filters are range-read predicates selecting keys created within a revision window; they have no meaning for a delete range. When applyOpts leaves ret.minCreateRev or ret.maxCreateRev non-zero, the constructor panics with "unexpected create revision filter in delete".

Source

Thrown at client/v3/op.go:291

	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
}

// OpPut returns "put" operation based on given key-value and operation options.
func OpPut(key, val string, opts ...OpOption) Op {
	ret := Op{t: tPut, key: []byte(key), val: []byte(val)}
	ret.applyOpts(opts)
	switch {
	case ret.end != nil:
		panic("unexpected range in put")
	case ret.limit != 0:
		panic("unexpected limit in put")
	case ret.rev != 0:

View on GitHub (pinned to f744d457f4)

Solutions

  1. List with Get + WithMaxCreateRev(cutoff), then delete those explicit keys in a Txn.
  2. If the condition is per-key, use a Txn If with CompareCreated instead.
  3. Strip create-rev filter options from Delete calls.

Example fix

// before
_, err := cli.Delete(ctx, "/sess/", clientv3.WithPrefix(), clientv3.WithMaxCreateRev(cutoffRev)) // panics

// after
gr, err := cli.Get(ctx, "/sess/", clientv3.WithPrefix(), clientv3.WithMaxCreateRev(cutoffRev))
var ops []clientv3.Op
for _, kv := range gr.Kvs {
    ops = append(ops, clientv3.OpDelete(string(kv.Key)))
}
_, err = cli.Txn(ctx).Then(ops...).Commit()
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: clientv3.OpDelete("/k/", clientv3.WithPrefix(), clientv3.WithMinCreateRev(rev)) — e.g. 'delete keys created before the backup revision'; option-slice sharing with a Get that used the same filter.

Common situations: Retention/GC jobs: list old keys via create-revision filters, then erroneously apply the filter to the cleanup Delete; generic option bundles in data-migration tooling.

Related errors


AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15). Data as JSON: /api/errors/4e89875d32f73e15. Report an issue: GitHub.