{"record":{"id":"70f596a5d9f1158a","repo":"pardeike/Harmony","slug":"delegate-typeof-delegatetype-has-no-defined-original-method","errorCode":null,"errorMessage":"Delegate {typeof(DelegateType)} has no defined original method","messagePattern":"Delegate (.+?) has no defined original method","errorType":"exception","errorClass":"NullReferenceException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":2003,"sourceCode":"\t\t/// Only applies for instance methods. If <c>null</c> (default), returned delegate is an open (a.k.a. unbound) instance delegate\r\n\t\t/// where an instance is supplied as the first argument to the delegate invocation; else, delegate is a closed (a.k.a. bound)\r\n\t\t/// instance delegate where the delegate invocation always applies to the given <paramref name=\"instance\"/>.\r\n\t\t/// </param>\r\n\t\t/// <returns>A delegate of given <typeparamref name=\"DelegateType\"/> to the method specified via [<see cref=\"HarmonyLib.HarmonyDelegate\"/>]\r\n\t\t/// attributes on <typeparamref name=\"DelegateType\"/></returns>\r\n\t\t/// <remarks>\r\n\t\t/// This calls <see cref=\"MethodDelegate{DelegateType}(MethodInfo, object, bool, Type[])\"/> with the <c>method</c> and <c>virtualCall</c> arguments\r\n\t\t/// determined from the [<see cref=\"HarmonyLib.HarmonyDelegate\"/>] attributes on <typeparamref name=\"DelegateType\"/>,\r\n\t\t/// and the given <paramref name=\"instance\"/> (for closed instance delegates).\r\n\t\t/// </remarks>\r\n\t\t///\r\n\t\tpublic static DelegateType HarmonyDelegate<DelegateType>(object instance = null) where DelegateType : Delegate\r\n\t\t{\r\n\t\t\tvar harmonyMethod = HarmonyMethodExtensions.GetMergedFromType(typeof(DelegateType));\r\n\t\t\tharmonyMethod.methodType ??= MethodType.Normal;\r\n\t\t\tvar method = harmonyMethod.GetOriginalMethod() as MethodInfo;\r\n\t\t\tif (method is null)\r\n\t\t\t\tthrow new NullReferenceException($\"Delegate {typeof(DelegateType)} has no defined original method\");\r\n\t\t\treturn MethodDelegate<DelegateType>(method, instance, harmonyMethod.nonVirtualDelegate is false, null);\r\n\t\t}\r\n\r\n\t\t/// <summary>Returns who called the current method</summary>\r\n\t\t/// <returns>The calling method/constructor (excluding the caller)</returns>\r\n\t\t///\r\n\t\tpublic static MethodBase GetOutsideCaller()\r\n\t\t{\r\n\t\t\tvar trace = new StackTrace(true);\r\n\t\t\tforeach (var frame in trace.GetFrames())\r\n\t\t\t{\r\n\t\t\t\tvar method = frame.GetMethod();\r\n\t\t\t\tif (method.DeclaringType?.Namespace != typeof(Harmony).Namespace)\r\n\t\t\t\t\treturn method;\r\n\t\t\t}\r\n\t\t\tthrow new Exception(\"Unexpected end of stack trace\");\r\n\t\t}\r\n\r","sourceCodeStart":1985,"sourceCodeEnd":2021,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L1985-L2021","documentation":"AccessTools.HarmonyDelegate<DelegateType> derives the patch target from [HarmonyMethod]-annotated members merged from the delegate type. It throws NullReferenceException when that merged HarmonyMethod yields no original MethodInfo, meaning the delegate type does not define (or fails to resolve) any target method.","triggerScenarios":"Calling AccessTools.HarmonyDelegate<T>() where typeof(T) has no attributes from which GetMergedFromType can build a HarmonyMethod, or the attributes specify an original method that resolves to null (e.g. wrong method name/type in the annotation).","commonSituations":"Forgetting to annotate the delegate type or its methods with [HarmonyMethod]/[HarmonyPatch], misspelling the target method name in the annotation, or the annotated declaring type not being loaded.","solutions":["Add or fix the [HarmonyMethod]/[HarmonyPatch] annotation on the delegate type specifying the original method.","Verify the annotated method name, declaring type, and argument types actually resolve via AccessTools.Method.","Use MethodDelegate directly with an explicit MethodInfo if dynamic target resolution is not needed."],"exampleFix":"// before\ndelegate int HealthGetter();\nvar d = AccessTools.HarmonyDelegate<HealthGetter>(entity);\n// after\n[HarmonyMethod(typeof(Player), nameof(Player.GetHealth))]\ndelegate int HealthGetter();\nvar d = AccessTools.HarmonyDelegate<HealthGetter>(entity);","handlingStrategy":"validation","validationCode":"var hm = HarmonyMethodExtensions.GetMergedFromType(typeof(DelegateType));\nif (hm.GetOriginalMethod() is not MethodInfo)\n    throw new InvalidOperationException(typeof(DelegateType) + \" has no [HarmonyMethod]/[HarmonyPatch]-defined original method\");","typeGuard":"bool DelegateHasTarget<D>() where D : Delegate => HarmonyMethodExtensions.GetMergedFromType(typeof(D)).GetOriginalMethod() is MethodInfo;","tryCatchPattern":"try { var d = AccessTools.HarmonyDelegate<T>(instance); }\ncatch (NullReferenceException) { throw new InvalidOperationException(\"Add [HarmonyMethod] annotation defining the original method on \" + typeof(T).Name); }","preventionTips":["Always annotate the delegate type (or its nested/associated members) with [HarmonyMethod] pointing at a resolvable method.","Verify the annotated target resolves: AccessTools.Method(declaringType, name) is not null.","Prefer MethodDelegate with an explicit MethodInfo for statically known targets."],"tags":["reflection","delegate","harmony-attributes","harmony"],"backgroundTag":"missing-required-argument","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"}