etcd-io/etcd · error
unexpected filter in put
Error message
unexpected filter in put
What it means
OpPut panics with 'unexpected filter in put' when WithFilterDelete or WithFilterPut is applied to a put. These filters tell a range/watch to drop deletion or put events from results and only make sense on queries; OpPut rejects them at construction time. The shared message 'unexpected filter in put' covers both filter flags.
Source
Thrown at client/v3/op.go:322
switch {
case ret.end != nil:
panic("unexpected range in put")
case ret.limit != 0:
panic("unexpected limit in put")
case ret.rev != 0:
panic("unexpected revision in put")
case ret.sort != nil:
panic("unexpected sort in put")
case ret.serializable:
panic("unexpected serializable in put")
case ret.countOnly:
panic("unexpected countOnly in put")
case ret.minModRev != 0, ret.maxModRev != 0:
panic("unexpected mod revision filter in put")
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
panic("unexpected create revision filter in put")
case ret.filterDelete, ret.filterPut:
panic("unexpected filter in put")
case ret.createdNotify:
panic("unexpected createdNotify in put")
}
return ret
}
// OpTxn returns "txn" operation based on given transaction conditions.
func OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {
clonedCmps := make([]Cmp, len(cmps))
for i := range cmps {
clonedCmps[i] = cmps[i].Clone()
}
return Op{t: tTxn, cmps: clonedCmps, thenOps: thenOps, elseOps: elseOps}
}
func OpWatch(key string, opts ...OpOption) Op {
ret := Op{t: tRange, key: []byte(key)}
ret.applyOpts(opts)View on GitHub (pinned to f744d457f4)
Solutions
- Remove WithFilterDelete/WithFilterPut from the put call
- Build the write op with zero options (a plain OpPut(key, val)) unless you specifically need WithLease or WithPrevKV, which are the put-valid ones
- Isolate watch/query option assembly from write option assembly in your codebase
- Check the panic family list in client/v3/op.go OpPut to see the full set of invalid options
Example fix
// before
op := clientv3.OpPut("k", "v", clientv3.WithFilterDelete(), clientv3.WithFilterPut())
// after
op := clientv3.OpPut("k", "v") Defensive patterns
Strategy: validation
Validate before calling
// Event filters are watch-only; keep them next to the Watch call.
wch := cli.Watch(ctx, "k", clientv3.WithFilterDelete(), clientv3.WithFilterPut())
op := clientv3.OpPut("k", "v") // plain put, no filters Prevention
- Scope WithFilter* to watcher setup code
- Write ops need at most WithLease/WithPrevKV/WithIgnore*
- Lint against shared variadic option forwarding
When it happens
Trigger: Calling clientv3.OpPut(key, val, clientv3.WithFilterDelete()) or WithFilterPut(). Typically the options were assembled for a watch or a Get over historical events and reused for a write, e.g. replaying an event log and persisting state with the same option slice.
Common situations: Watch-event processors that read with event filters and then write derived state; option slices shared between watch setup and put; migrating from KV.Get to explicit Op construction while keeping old options.
Related errors
- unexpected sort in put
- unexpected serializable in put
- unexpected countOnly in put
- unexpected mod revision filter in put
- unexpected create revision filter in put
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/f384af15652505d5.
Report an issue: GitHub.