etcd-io/etcd · error
unexpected limit in put
Error message
unexpected limit in put
What it means
OpPut rejects WithLimit because limit bounds the number of results of a range read; a put returns no result set. After applyOpts, the validation switch sees ret.limit != 0 and panics with "unexpected limit in put". Like the other put guards, it exists to surface option misuse (typically a Get's option list reused for a Put) at construction time.
Source
Thrown at client/v3/op.go:308
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")
}
return retView on GitHub (pinned to f744d457f4)
Solutions
- Remove WithLimit from Put calls.
- Keep the Get's pagination options in a distinct variable from the Put's options (e.g. getOpts vs putOpts).
- If you intended bounded writes, bound them client-side by chunking keys into Txn batches.
Example fix
// before
opts := []clientv3.OpOption{clientv3.WithLimit(100), clientv3.WithPrefix()}
cli.Get(ctx, "/q/", opts...)
_, err := cli.Put(ctx, "/q/done", "1", opts...) // panics: unexpected limit in put
// after
getOpts := []clientv3.OpOption{clientv3.WithLimit(100), clientv3.WithPrefix()}
cli.Get(ctx, "/q/", getOpts...)
_, err := cli.Put(ctx, "/q/done", "1") Defensive patterns
Strategy: validation
Prevention
- WithLimit is a Get pagination option; a Put has no result set to cap.
- After a paginated Get, write back with a fresh, empty option list.
- Name option variables by purpose (getOpts vs putOpts) so reuse bugs are visible in review.
When it happens
Trigger: clientv3.OpPut("/k", "v", clientv3.WithLimit(10)); forwarding pagination options from a listing Get into the subsequent write-back Put; shared []OpOption bundles.
Common situations: Batch-processing loops that list with WithLimit and write results back using the same options variable; generic CRUD wrappers with one options parameter for all methods.
Related errors
- unexpected limit in delete
- unexpected range in put
- unexpected revision in put
- unexpected lease in delete
- unexpected revision in delete
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/92d246d5ccd01702.
Report an issue: GitHub.