{"record":{"id":"899804528a7cff0d","repo":"pardeike/Harmony","slug":"invalid-delegate-type","errorCode":null,"errorMessage":"Invalid delegate type","messagePattern":"Invalid delegate type","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":1817,"sourceCode":"\t\t\t\treturn (DelegateType)Delegate.CreateDelegate(delegateType, method);\r\n\t\t\t}\r\n\r\n\t\t\tvar declaringType = method.DeclaringType;\r\n\t\t\tif (declaringType != null && declaringType.IsInterface && !virtualCall)\r\n\t\t\t{\r\n\t\t\t\tthrow new ArgumentException(\"Interface methods must be called virtually\");\r\n\t\t\t}\r\n\r\n\t\t\t// Open instance method delegate ...\r\n\t\t\tif (instance is null)\r\n\t\t\t{\r\n\t\t\t\tvar delegateParameters = delegateType.GetMethod(\"Invoke\").GetParameters();\r\n\t\t\t\tif (delegateParameters.Length == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Following should throw an ArgumentException with the proper message string.\r\n\t\t\t\t\t_ = Delegate.CreateDelegate(typeof(DelegateType), method);\r\n\t\t\t\t\t// But in case it doesn't...\r\n\t\t\t\t\tthrow new ArgumentException(\"Invalid delegate type\");\r\n\t\t\t\t}\r\n\t\t\t\tvar delegateInstanceType = delegateParameters[0].ParameterType;\r\n\t\t\t\t// Exceptional case: delegate struct instance type cannot be created from an interface method.\r\n\t\t\t\t// This case is handled in the \"non-virtual call\" case, using the struct method and the matching delegate instance type.\r\n\t\t\t\tif (declaringType != null && declaringType.IsInterface && delegateInstanceType.IsValueType)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar interfaceMapping = delegateInstanceType.GetInterfaceMap(declaringType);\r\n\t\t\t\t\tmethod = interfaceMapping.TargetMethods[Array.IndexOf(interfaceMapping.InterfaceMethods, method)];\r\n\t\t\t\t\tdeclaringType = delegateInstanceType;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// ... that virtually calls ...\r\n\t\t\t\tif (declaringType != null && virtualCall)\r\n\t\t\t\t{\r\n\t\t\t\t\t// ... an interface method\r\n\t\t\t\t\t// If method is already an interface method, just create a delegate from it directly.\r\n\t\t\t\t\tif (declaringType.IsInterface)\r\n\t\t\t\t\t{\r","sourceCodeStart":1799,"sourceCodeEnd":1835,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L1799-L1835","documentation":"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.","triggerScenarios":"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.","commonSituations":"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>.","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."],"exampleFix":"// before\nvar d = AccessTools.MethodDelegate<Func<int>>(getNameMethod);\n// after\nvar d = AccessTools.MethodDelegate<Func<Entity, string>>(getNameMethod);","handlingStrategy":"type-guard","validationCode":"var invoke = delegateType.GetMethod(\"Invoke\");\nvar isInstanceMethod = !method.IsStatic;\nif (instance is null && isInstanceMethod && invoke.GetParameters().Length == 0)\n    throw new InvalidOperationException(\"Need Func<TInstance, ...> delegate or a closed instance\");","typeGuard":"bool IsValidOpenDelegate(DelegateType d, MethodInfo m) => d.GetMethod(\"Invoke\").GetParameters().Length == (m.IsStatic ? 0 : 1);","tryCatchPattern":"try { var d = AccessTools.MethodDelegate<T>(method); }\ncatch (ArgumentException) { throw new InvalidOperationException(\"Delegate signature must include the instance as first parameter\"); }","preventionTips":["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."],"tags":["reflection","delegate","harmony"],"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"}