etcd-io/etcd · error

unexpected create revision filter in watch

Error message

unexpected create revision filter in watch

What it means

OpWatch panics with 'unexpected create revision filter in watch' when WithMinCreateRev or WithMaxCreateRev is applied to a watch. Create-revision filters narrow range results by when keys were created; watches have no such filtering, so OpWatch's validation switch rejects them with this panic before any stream opens.

Source

Thrown at client/v3/op.go:355

func OpWatch(key string, opts ...OpOption) Op {
	ret := Op{t: tRange, key: []byte(key)}
	ret.applyOpts(opts)
	switch {
	case ret.leaseID != 0:
		panic("unexpected lease in watch")
	case ret.limit != 0:
		panic("unexpected limit in watch")
	case ret.sort != nil:
		panic("unexpected sort in watch")
	case ret.serializable:
		panic("unexpected serializable in watch")
	case ret.countOnly:
		panic("unexpected countOnly in watch")
	case ret.minModRev != 0, ret.maxModRev != 0:
		panic("unexpected mod revision filter in watch")
	case ret.minCreateRev != 0, ret.maxCreateRev != 0:
		panic("unexpected create revision filter in watch")
	}
	return ret
}

func (op *Op) applyOpts(opts []OpOption) {
	for _, opt := range opts {
		opt(op)
	}
}

// OpOption configures Operations like Get, Put, Delete.
type OpOption func(*Op)

// WithLease attaches a lease ID to a key in 'Put' request.
func WithLease(leaseID LeaseID) OpOption {
	return func(op *Op) { op.leaseID = leaseID }
}

View on GitHub (pinned to f744d457f4)

Solutions

  1. Remove WithMinCreateRev/WithMaxCreateRev from the watch call
  2. Filter create revisions client-side on incoming watch events using Kv.CreateRevision, or seed state with the filtered Get first
  3. Maintain distinct builders for listing options and watch options
  4. Add tests constructing all watches used by the application to fail fast in CI

Example fix

// before
op := clientv3.OpWatch("k", clientv3.WithMinCreateRev(1000))

// after
wop := clientv3.OpWatch("k", clientv3.WithPrefix())
// filter in handler: if ev.Kv.CreateRevision >= 1000 { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Filter created-revision client-side in the event handler:
for ev := range wch {
	if ev.Kv.CreateRevision >= minCreateRev {
		handle(ev)
	}
}

Prevention

When it happens

Trigger: Calling clientv3.OpWatch(key, clientv3.WithMinCreateRev(n)) or WithMaxCreateRev(n); reusing options from a listing query that filters keys by creation time when subscribing to the same keys; unified option structs mapped onto every operation type.

Common situations: 'Keys created since X' audit features that both list (filtered Get) and subscribe; copy-paste between adjacent listing and watching code; refactors that collapsed option handling into one helper.

Related errors


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