etcd-io/etcd · error

unexpected sort in delete

Error message

unexpected sort in delete

What it means

OpDelete rejects WithSort / any sort option (WithSortKeys, WithSortByXXX orderings set ret.sort). Sorting orders the KV results of a range read; a delete returns only a count of removed keys, so a sort is inapplicable. The constructor's validation switch panics with "unexpected sort in delete" rather than dropping the option.

Solutions

  1. Remove the WithSort option from Delete calls.
  2. If you intended 'delete oldest/newest first', page with Get+WithSort and delete the explicit keys in a Txn instead.
  3. Keep listing options and mutation options in distinct slices/types at the call site.

Example fix

// before
_, err := cli.Delete(ctx, "/logs/", clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend)) // panics

// after
_, err := cli.Delete(ctx, "/logs/", clientv3.WithPrefix())
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: clientv3.OpDelete("/k/", clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)); forwarding a Get's ordering options into a cleanup Delete; table-driven code that applies one option set to every operation.

Common situations: Shared option helpers; copy-paste from a listing routine into the matching purge routine; IDE auto-completing sort options into a delete call.

Related errors


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

Appendix: source

Thrown at client/v3/op.go:283

}

// 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 {

View on GitHub (pinned to f744d457f4)