etcd-io/etcd · error
unexpected range in put
Error message
unexpected range in put
What it means
clientv3.OpPut (and cli.Put, which delegates to it) panics with "unexpected range in put" when the option list sets an end key: WithRange, WithFromKey, or WithPrefix all set op.end. A put writes a single key; a range end is inapplicable, so the constructor rejects it rather than silently ignoring it.
Source
Thrown at client/v3/op.go:306
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:
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")View on GitHub (pinned to f744d457f4)
Solutions
- Remove WithRange/WithPrefix/WithFromKey from Put calls — a put targets exactly one key.
- To put many keys, build []Op of OpPut per key and submit via cli.Txn(ctx).Then(ops...).Commit().
- Separate option slices per operation kind in shared helpers.
Example fix
// before _, err := cli.Put(ctx, "/cfg/", newVal, clientv3.WithPrefix()) // panics: unexpected range in put // after _, err := cli.Put(ctx, "/cfg/defaults", newVal)
Defensive patterns
Strategy: validation
Prevention
- A Put targets exactly one key: WithRange/WithPrefix/WithFromKey never belong on it.
- For multi-key writes, build []clientv3.Op of OpPut and submit via Txn Then(...).
- In generic helpers, partition options into getOpts/putOpts/delOpts at the boundary.
When it happens
Trigger: clientv3.OpPut("/k", "v", clientv3.WithRange("/z")) or reusing a Get/Delete option slice (containing WithPrefix/WithFromKey/WithRange) for a Put; generic helper APIs like do(opType, key, val, opts...) that forward the same options to every operation kind.
Common situations: Read-modify-write flows where the read used WithPrefix and the option slice is reused; utility wrappers that accept OpOption variadics for both Put and Get; copy-paste between a bulk delete and a status-marker put.
Related errors
- unexpected limit in put
- unexpected revision in put
- `WithPrefix` and `WithFromKey` cannot be set at the same tim
- unexpected lease in delete
- unexpected limit in delete
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/90adc8e3374d968c.
Report an issue: GitHub.