{"record":{"id":"226da0442d035c31","repo":"etcd-io/etcd","slug":"unknown-result-op","errorCode":null,"errorMessage":"Unknown result op","messagePattern":"Unknown result op","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/v3/compare.go","lineNumber":70,"sourceCode":"\t\treturn Cmp{}\n\t}\n\treturn Cmp{c: cloneCompare(cmp.c)}\n}\n\nfunc Compare(cmp Cmp, result string, v any) Cmp {\n\tvar r pb.Compare_CompareResult\n\n\tswitch result {\n\tcase \"=\":\n\t\tr = pb.Compare_EQUAL\n\tcase \"!=\":\n\t\tr = pb.Compare_NOT_EQUAL\n\tcase \">\":\n\t\tr = pb.Compare_GREATER\n\tcase \"<\":\n\t\tr = pb.Compare_LESS\n\tdefault:\n\t\tpanic(\"Unknown result op\")\n\t}\n\n\tcmp = cmp.Clone()\n\tcmp.ensureCompare()\n\tcmp.c.Result = r\n\tswitch cmp.c.Target {\n\tcase pb.Compare_VALUE:\n\t\tval, ok := v.(string)\n\t\tif !ok {\n\t\t\tpanic(\"bad compare value\")\n\t\t}\n\t\tcmp.c.TargetUnion = &pb.Compare_Value{Value: []byte(val)}\n\tcase pb.Compare_VERSION:\n\t\tcmp.c.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}\n\tcase pb.Compare_CREATE:\n\t\tcmp.c.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}\n\tcase pb.Compare_MOD:\n\t\tcmp.c.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/etcd-io/etcd/blob/f744d457f484e9f748a0700b48ef96dcf792df33/client/v3/compare.go#L52-L88","documentation":"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.","triggerScenarios":"Calling clientv3.Compare(clientv3.CompareValue(key), \">=\", \"val\"), or using \"==\", \"<>\", \"=>\", \"=!\", or an empty/typo'd operator string. Only exact strings \"=\", \"!=\", \">\", \"<\" are accepted.","commonSituations":"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).","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."],"exampleFix":"// before\ncmp := clientv3.Compare(clientv3.CompareValue(key), \">=\", \"10\") // panics: Unknown result op\n\n// after\ncmp := clientv3.Compare(clientv3.CompareValue(key), \">\", \"9\") // expresses >= 10 for string compare, or use two Cmps in Txn().If","handlingStrategy":"validation","validationCode":"var validResultOps = map[string]bool{\"=\": true, \"!=\": true, \">\": true, \"<\": true}\n\nfunc validCompareOp(op string) bool { return validResultOps[op] }\n\n// use: if !validCompareOp(userOp) { return errors.New(\"unsupported compare op: \" + userOp) }","typeGuard":"func isCompareResultOp(s string) bool {\n\tswitch s {\n\tcase \"=\", \"!=\", \">\", \"<\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"// Go has no catch; recover only at a high boundary if the op string is external:\ndefer func() {\n    if r := recover(); r != nil {\n        return fmt.Errorf(\"clientv3.Compare panicked: %v\", r)\n    }\n}()\ncmp := clientv3.Compare(target, op, val)","preventionTips":["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."],"tags":["go","etcd","clientv3","txn","compare","panic","invalid-argument"],"backgroundTag":null,"analyzedSha":"f744d457f484e9f748a0700b48ef96dcf792df33","analyzedAt":"2026-08-15T09:39:50.079Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}