etcd-io/etcd · error
unexpected serializable in delete
Error message
unexpected serializable in delete
What it means
OpDelete rejects WithSerializable. Serializable reads may be served by any member without quorum confirmation; deletes are writes and always go through the raft quorum, so a 'serializable delete' has no semantics. After applyOpts sets ret.serializable, the validation switch panics with "unexpected serializable in delete".
Source
Thrown at client/v3/op.go:285
// 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
}
// 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)View on GitHub (pinned to f744d457f4)
Solutions
- Remove WithSerializable from Delete calls — every write is linearizable by definition.
- Apply WithSerializable only at Get call sites (typically with WithSerializable reads for stale-tolerant dashboards).
Example fix
// before _, err := cli.Delete(ctx, cacheKey, clientv3.WithSerializable()) // panics // after _, err := cli.Delete(ctx, cacheKey)
Defensive patterns
Strategy: validation
Prevention
- WithSerializable only relaxes read consistency; writes are always linearizable.
- Audit cache/eviction helpers that add 'performance' options uniformly to all ops.
- Apply WithSerializable only next to Get (and WithSerializable-friendly Watch) calls.
When it happens
Trigger: clientv3.OpDelete(k, clientv3.WithSerializable()); forwarding read-path options (built to lower read latency) into the matching invalidation Delete; config-driven option bundles applied uniformly to all ops.
Common situations: Read-your-writes caches that use WithSerializable on Get and reuse the same helper for eviction deletes; templates that append 'performance' options to every clientv3 call.
Related errors
- unexpected lease in delete
- unexpected limit in delete
- unexpected revision in delete
- unexpected sort in delete
- unexpected countOnly in delete
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/7b0caf4150cc991e.
Report an issue: GitHub.