etcd-io/etcd · error
Unknown result op
Error message
Unknown result op
What it means
clientv3.Compare(cmp, result, v) panics with "Unknown result op" when the result string is not one of the four accepted comparison operators. The switch only maps "=", "!=", ">", and "<" to pb.Compare_EQUAL/NOT_EQUAL/GREATER/LESS; anything else falls through to the default case. This is a fail-fast check on a programmer-supplied string, not a runtime/server condition.
Source
Thrown at client/v3/compare.go:70
return Cmp{}
}
return Cmp{c: cloneCompare(cmp.c)}
}
func Compare(cmp Cmp, result string, v any) Cmp {
var r pb.Compare_CompareResult
switch result {
case "=":
r = pb.Compare_EQUAL
case "!=":
r = pb.Compare_NOT_EQUAL
case ">":
r = pb.Compare_GREATER
case "<":
r = pb.Compare_LESS
default:
panic("Unknown result op")
}
cmp = cmp.Clone()
cmp.ensureCompare()
cmp.c.Result = r
switch cmp.c.Target {
case pb.Compare_VALUE:
val, ok := v.(string)
if !ok {
panic("bad compare value")
}
cmp.c.TargetUnion = &pb.Compare_Value{Value: []byte(val)}
case pb.Compare_VERSION:
cmp.c.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}
case pb.Compare_CREATE:
cmp.c.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
case pb.Compare_MOD:
cmp.c.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}View on GitHub (pinned to f744d457f4)
Solutions
- Use exactly one of the four strings: "=", "!=", ">", "<" — e.g. clientv3.Compare(clientv3.CompareValue(key), "=", "expected").
- If you need >= or <=, express it with the available operators or combine two Cmp conditions with clientv3.Compare(...) joined in clientv3.Txn().If(cmp1, cmp2).
- If the operator comes from external input, validate it against a whitelist {"=", "!=", ">", "<"} before calling Compare.
- Wrap the call in a recover() only as a last-resort guard for input-driven operator strings.
Example fix
// before cmp := clientv3.Compare(clientv3.CompareValue(key), ">=", "10") // panics: Unknown result op // after cmp := clientv3.Compare(clientv3.CompareValue(key), ">", "9") // expresses >= 10 for string compare, or use two Cmps in Txn().If
Defensive patterns
Strategy: validation
Validate before calling
var validResultOps = map[string]bool{"=": true, "!=": true, ">": true, "<": true}
func validCompareOp(op string) bool { return validResultOps[op] }
// use: if !validCompareOp(userOp) { return errors.New("unsupported compare op: " + userOp) } Type guard
func isCompareResultOp(s string) bool {
switch s {
case "=", "!=", ">", "<":
return true
}
return false
} Try / catch
// Go has no catch; recover only at a high boundary if the op string is external:
defer func() {
if r := recover(); r != nil {
return fmt.Errorf("clientv3.Compare panicked: %v", r)
}
}()
cmp := clientv3.Compare(target, op, val) Prevention
- Treat the operator as a closed enum: define package-level constants (OpEqual = "=" etc.) and use only those.
- Never pass user/config strings straight into Compare; whitelist them first.
- Remember etcd has no >= or <=: express them with > / < on the neighbor value or multiple Cmp conditions.
When it happens
Trigger: Calling clientv3.Compare(clientv3.CompareValue(key), ">=", "val"), or using "==", "<>", "=>", "=!", or an empty/typo'd operator string. Only exact strings "=", "!=", ">", "<" are accepted.
Common situations: Developers instinctively write "==" (C/Go habit) or ">="/"<=" expecting etcd to support them; building the operator from user input or config that uses different symbols; copying example code from other etcd client libraries (e.g. jetcd uses different enum names).
Related errors
- bad compare value
- bad value %v of type %T
- unsupported stm
- unexpected revision = 0. Calling SyncUpdates before SyncBase
- `WithPrefix` and `WithFromKey` cannot be set at the same tim
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/226da0442d035c31.
Report an issue: GitHub.