pardeike/Harmony · error · ArgumentException
Invalid delegate type
Error message
Invalid delegate type
What it means
AccessTools.MethodDelegate throws this ArgumentException when creating an open-instance delegate whose delegate signature has no first parameter to hold the instance. Delegate.CreateDelegate should throw a proper ArgumentException in that case; Harmony adds this as a fallback in case the runtime does not throw.
Solutions
- Use a delegate type whose first parameter is the instance type (open-instance delegate), e.g. Func<MyClass, int> instead of Func<int>.
- Or pass a specific instance to MethodDelegate so a closed delegate can be created.
- Or wrap a static method if a parameterless delegate is intended.
Example fix
// before var d = AccessTools.MethodDelegate<Func<int>>(getNameMethod); // after var d = AccessTools.MethodDelegate<Func<Entity, string>>(getNameMethod);
Defensive patterns
Strategy: type-guard
Validate before calling
var invoke = delegateType.GetMethod("Invoke");
var isInstanceMethod = !method.IsStatic;
if (instance is null && isInstanceMethod && invoke.GetParameters().Length == 0)
throw new InvalidOperationException("Need Func<TInstance, ...> delegate or a closed instance"); Type guard
bool IsValidOpenDelegate(DelegateType d, MethodInfo m) => d.GetMethod("Invoke").GetParameters().Length == (m.IsStatic ? 0 : 1); Try / catch
try { var d = AccessTools.MethodDelegate<T>(method); }
catch (ArgumentException) { throw new InvalidOperationException("Delegate signature must include the instance as first parameter"); } Prevention
- For open-instance delegates, make the first delegate parameter the instance type.
- Match delegate parameter count to method staticness (static: 0 extra, instance: 1 extra).
- Pass an explicit instance if you want a closed delegate with no instance parameter.
When it happens
Trigger: Calling AccessTools.MethodDelegate<DelegateType>(method) with instance == null where the delegate type's Invoke has zero parameters but the method is an open-instance (non-static) method requiring an instance argument.
Common situations: Using a parameterless delegate like Func<int> for an instance method instead of a delegate whose first parameter is the instance type, e.g. Func<MyClass, int>.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Interface methods must be called virtually
- Delegate has no defined original method
- 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/899804528a7cff0d.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:1817
return (DelegateType)Delegate.CreateDelegate(delegateType, method);
}
var declaringType = method.DeclaringType;
if (declaringType != null && declaringType.IsInterface && !virtualCall)
{
throw new ArgumentException("Interface methods must be called virtually");
}
// Open instance method delegate ...
if (instance is null)
{
var delegateParameters = delegateType.GetMethod("Invoke").GetParameters();
if (delegateParameters.Length == 0)
{
// Following should throw an ArgumentException with the proper message string.
_ = Delegate.CreateDelegate(typeof(DelegateType), method);
// But in case it doesn't...
throw new ArgumentException("Invalid delegate type");
}
var delegateInstanceType = delegateParameters[0].ParameterType;
// Exceptional case: delegate struct instance type cannot be created from an interface method.
// This case is handled in the "non-virtual call" case, using the struct method and the matching delegate instance type.
if (declaringType != null && declaringType.IsInterface && delegateInstanceType.IsValueType)
{
var interfaceMapping = delegateInstanceType.GetInterfaceMap(declaringType);
method = interfaceMapping.TargetMethods[Array.IndexOf(interfaceMapping.InterfaceMethods, method)];
declaringType = delegateInstanceType;
}
// ... that virtually calls ...
if (declaringType != null && virtualCall)
{
// ... an interface method
// If method is already an interface method, just create a delegate from it directly.
if (declaringType.IsInterface)
{
View on GitHub (pinned to e7872dc170)