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

  1. Remove WithRange/WithPrefix/WithFromKey from Put calls — a put targets exactly one key.
  2. To put many keys, build []Op of OpPut per key and submit via cli.Txn(ctx).Then(ops...).Commit().
  3. 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

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


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