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

  1. Use exactly one of the four strings: "=", "!=", ">", "<" — e.g. clientv3.Compare(clientv3.CompareValue(key), "=", "expected").
  2. 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).
  3. If the operator comes from external input, validate it against a whitelist {"=", "!=", ">", "<"} before calling Compare.
  4. 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

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


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