{"record":{"id":"6bb138c799a0bcf4","repo":"pardeike/Harmony","slug":"ambiguous-match-for-harmonymethod-attr-description","errorCode":null,"errorMessage":"Ambiguous match for HarmonyMethod[{attr.Description()}]","messagePattern":"Ambiguous match for HarmonyMethod\\[(.+?)\\]","errorType":"exception","errorClass":"HarmonyException","httpStatus":null,"severity":"error","filePath":"Harmony/Internal/PatchTools.cs","lineNumber":203,"sourceCode":"\t\t\t\t\tcase MethodType.OperatorBitwiseAnd:\r\n\t\t\t\t\tcase MethodType.OperatorBitwiseOr:\r\n\t\t\t\t\tcase MethodType.OperatorExclusiveOr:\r\n\t\t\t\t\tcase MethodType.OperatorLeftShift:\r\n\t\t\t\t\tcase MethodType.OperatorRightShift:\r\n\t\t\t\t\tcase MethodType.OperatorEquality:\r\n\t\t\t\t\tcase MethodType.OperatorInequality:\r\n\t\t\t\t\tcase MethodType.OperatorGreaterThan:\r\n\t\t\t\t\tcase MethodType.OperatorLessThan:\r\n\t\t\t\t\tcase MethodType.OperatorGreaterThanOrEqual:\r\n\t\t\t\t\tcase MethodType.OperatorLessThanOrEqual:\r\n\t\t\t\t\tcase MethodType.OperatorComma:\r\n\t\t\t\t\t\tvar methodName = \"op_\" + attr.methodType.ToString().Replace(\"Operator\", \"\");\r\n\t\t\t\t\t\treturn AccessTools.DeclaredMethod(attr.declaringType, methodName, attr.argumentTypes);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch (AmbiguousMatchException ex)\r\n\t\t\t{\r\n\t\t\t\tthrow new HarmonyException($\"Ambiguous match for HarmonyMethod[{attr.Description()}]\", ex.InnerException ?? ex);\r\n\t\t\t}\r\n\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n}\r\n","sourceCodeStart":185,"sourceCodeEnd":210,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Internal/PatchTools.cs#L185-L210","documentation":"Harmony's GetOriginalMethod resolves the original method from a HarmonyMethod attribute, e.g. for patching operators like op_Addition. When AccessTools.DeclaredMethod finds multiple overloads matching the name (and optionally argument types), .NET throws AmbiguousMatchException, which Harmony wraps in this HarmonyException so the attribute description is included.","triggerScenarios":"Patching a type that declares an overloaded method via attr.methodType (e.g. methodType derived from a name like 'Add') or an operator/overload where attr.argumentTypes is null or not specific enough, so more than one declared method matches the resolved name.","commonSituations":"Patching operator overloads (e.g. op_Addition with multiple implicit-conversion-compatible operand combinations), patching overloaded methods by name without specifying argumentTypes, and switching .NET versions where an extra overload was added to a base class library type.","solutions":["Set attr.argumentTypes to the exact parameter types of the overload you want to patch","If patching an operator, verify the operator does not have multiple implementations (e.g. lhs/rhs vs lifted nullable forms); pick argumentTypes accordingly","Catch HarmonyException and inspect the inner AmbiguousMatchException plus the attribute Description() to see which method name/type was ambiguous","Patch the specific overload via a HarmonyMethod constructed from a MethodInfo instead of by name"],"exampleFix":"// before\n[HarmonyPatch(typeof(Vector3), MethodType.Normal, nameof(Vector3.Add))]\n// after\n[HarmonyPatch(typeof(Vector3), nameof(Vector3.Add), new[] { typeof(Vector3) })]","handlingStrategy":"validation","validationCode":"var mi = AccessTools.DeclaredMethod(attr.declaringType, \"op_\" + attr.methodType.ToString().Replace(\"Operator\", \"\"), attr.argumentTypes);\nif (mi is null) throw new InvalidOperationException($\"No unique method for {attr.Description()}; specify argumentTypes\");","typeGuard":"bool IsResolvable(HarmonyMethod attr) => AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes) is not null;","tryCatchPattern":"try { PatchOriginalMethod(attr); } catch (HarmonyException ex) when (ex.InnerException is AmbiguousMatchException) { log.Error($\"Ambiguous patch target: {ex.Message}\", ex); }","preventionTips":["Always specify argumentTypes when patching overloaded methods or operators","Prefer patching via MethodInfo over name-based resolution","Re-run patches in CI against every supported target-assembly version"],"tags":["reflection","ambiguity","patching","csharp"],"backgroundTag":"invalid-argument-value","analyzedSha":"e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c","analyzedAt":"2026-09-15T22:47:11.550Z","contentChangedAt":"2026-09-15T22:47:11.550Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}