pardeike/Harmony · error · NullReferenceException
Delegate has no defined original method
Error message
Delegate {typeof(DelegateType)} has no defined original method What it means
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.
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.
Example fix
// before delegate int HealthGetter(); var d = AccessTools.HarmonyDelegate<HealthGetter>(entity); // after [HarmonyMethod(typeof(Player), nameof(Player.GetHealth))] delegate int HealthGetter(); var d = AccessTools.HarmonyDelegate<HealthGetter>(entity);
Defensive patterns
Strategy: validation
Validate before calling
var hm = HarmonyMethodExtensions.GetMergedFromType(typeof(DelegateType));
if (hm.GetOriginalMethod() is not MethodInfo)
throw new InvalidOperationException(typeof(DelegateType) + " has no [HarmonyMethod]/[HarmonyPatch]-defined original method"); Type guard
bool DelegateHasTarget<D>() where D : Delegate => HarmonyMethodExtensions.GetMergedFromType(typeof(D)).GetOriginalMethod() is MethodInfo;
Try / catch
try { var d = AccessTools.HarmonyDelegate<T>(instance); }
catch (NullReferenceException) { throw new InvalidOperationException("Add [HarmonyMethod] annotation defining the original method on " + typeof(T).Name); } Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Interface methods must be called virtually
- Invalid delegate type
- The type must declare an empty constructor (the constructor…
- Value cannot be null. (Parameter 'fromMethod')
- Value cannot be null. (Parameter 'method')
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/70f596a5d9f1158a.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:2003
/// Only applies for instance methods. If <c>null</c> (default), returned delegate is an open (a.k.a. unbound) instance delegate
/// where an instance is supplied as the first argument to the delegate invocation; else, delegate is a closed (a.k.a. bound)
/// instance delegate where the delegate invocation always applies to the given <paramref name="instance"/>.
/// </param>
/// <returns>A delegate of given <typeparamref name="DelegateType"/> to the method specified via [<see cref="HarmonyLib.HarmonyDelegate"/>]
/// attributes on <typeparamref name="DelegateType"/></returns>
/// <remarks>
/// This calls <see cref="MethodDelegate{DelegateType}(MethodInfo, object, bool, Type[])"/> with the <c>method</c> and <c>virtualCall</c> arguments
/// determined from the [<see cref="HarmonyLib.HarmonyDelegate"/>] attributes on <typeparamref name="DelegateType"/>,
/// and the given <paramref name="instance"/> (for closed instance delegates).
/// </remarks>
///
public static DelegateType HarmonyDelegate<DelegateType>(object instance = null) where DelegateType : Delegate
{
var harmonyMethod = HarmonyMethodExtensions.GetMergedFromType(typeof(DelegateType));
harmonyMethod.methodType ??= MethodType.Normal;
var method = harmonyMethod.GetOriginalMethod() as MethodInfo;
if (method is null)
throw new NullReferenceException($"Delegate {typeof(DelegateType)} has no defined original method");
return MethodDelegate<DelegateType>(method, instance, harmonyMethod.nonVirtualDelegate is false, null);
}
/// <summary>Returns who called the current method</summary>
/// <returns>The calling method/constructor (excluding the caller)</returns>
///
public static MethodBase GetOutsideCaller()
{
var trace = new StackTrace(true);
foreach (var frame in trace.GetFrames())
{
var method = frame.GetMethod();
if (method.DeclaringType?.Namespace != typeof(Harmony).Namespace)
return method;
}
throw new Exception("Unexpected end of stack trace");
}
View on GitHub (pinned to e7872dc170)