{"record":{"id":"523f7f3d7aab65c3","repo":"pardeike/Harmony","slug":"cannot-directly-reference-dynamic-method-patch","errorCode":null,"errorMessage":"Cannot directly reference dynamic method \\\"{patch.FullDescription()}\\\" in Harmony. Use a factory method instead that will return the dynamic method.","messagePattern":"Cannot directly reference dynamic method \\\\\"(.+?)\\\\\" in Harmony\\. Use a factory method instead that will return the dynamic method\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Harmony/Public/Patch.cs","lineNumber":83,"sourceCode":"\t\t\t{\r\n\t\t\t\tpatchMethod = value;\r\n\t\t\t\tmethodToken = patchMethod.MetadataToken;\r\n\t\t\t\tmoduleGUID = patchMethod.Module.ModuleVersionId.ToString();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Creates a patch</summary>\r\n\t\t/// <param name=\"patch\">The method of the patch</param>\r\n\t\t/// <param name=\"index\">Zero-based index</param>\r\n\t\t/// <param name=\"owner\">An owner (Harmony ID)</param>\r\n\t\t/// <param name=\"priority\">The priority, see <see cref=\"Priority\"/></param>\r\n\t\t/// <param name=\"before\">A list of Harmony IDs for patches that should run after this patch</param>\r\n\t\t/// <param name=\"after\">A list of Harmony IDs for patches that should run before this patch</param>\r\n\t\t/// <param name=\"debug\">A flag that will log the replacement method via <see cref=\"FileLog\"/> every time this patch is used to build the replacement, even in the future</param>\r\n\t\t///\r\n\t\tpublic Patch(MethodInfo patch, int index, string owner, int priority, string[] before, string[] after, bool debug)\r\n\t\t{\r\n\t\t\tif (patch is DynamicMethod) throw new Exception($\"Cannot directly reference dynamic method \\\"{patch.FullDescription()}\\\" in Harmony. Use a factory method instead that will return the dynamic method.\");\r\n\r\n\t\t\tthis.index = index;\r\n\t\t\tthis.owner = owner;\r\n\t\t\tthis.priority = priority == -1 ? Priority.Normal : priority;\r\n\t\t\tthis.before = before ?? [];\r\n\t\t\tthis.after = after ?? [];\r\n\t\t\tthis.debug = debug;\r\n\t\t\tPatchMethod = patch;\r\n\t\t}\r\n\r\n\t\t/// <summary>Creates a patch</summary>\r\n\t\t/// <param name=\"method\">The method of the patch</param>\r\n\t\t/// <param name=\"index\">Zero-based index</param>\r\n\t\t/// <param name=\"owner\">An owner (Harmony ID)</param>\r\n\t\tpublic Patch(HarmonyMethod method, int index, string owner)\r\n\t\t\t: this(method.method, index, owner, method.priority, method.before, method.after, method.debug ?? false) { }\r\n\r\n\t\tinternal int MethodToken => methodToken;\r","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Public/Patch.cs#L65-L101","documentation":"A DynamicMethod cannot be referenced directly as a patch in Harmony because its IL body may be freed/recollected before the patch is applied. Harmony requires a factory method (a Func<DynamicMethod>) so the dynamic method can be re-emitted on demand, e.g. when patches are rebuilt or unpatched/repatched.","triggerScenarios":"Passing a System.Reflection.Emit.DynamicMethod to the Patch(MethodInfo, int, string, int, string[], string[], bool) constructor, directly or via PatchMethods/ManualPatchMethods lists.","commonSituations":"Building patches at runtime with Reflection.Emit; migrating code that previously used MethodInfo everywhere; storing dynamic methods created by other libraries (e.g. old serialization frameworks) and feeding them to Harmony.","solutions":["Pass a factory method instead: a MethodInfo returning DynamicMethod (with optional object[] parameter) so Harmony can re-create the method on demand","If the method is truly static and long-lived, compile it to a normal method or use a static wrapper method as the patch","Use PatchTools.GetPatchMethod / factory-style helper to register the dynamic method"],"exampleFix":"// before\nDynamicMethod dm = EmitReplacement();\nnew Patch(dm, 0, null, -1, null, null, false);\n// after\nstatic DynamicMethod Factory() => EmitReplacement();\nnew Patch(typeof(Patches).GetMethod(nameof(Factory), BindingFlags.NonPublic | BindingFlags.Static), 0, null, -1, null, null, false);","handlingStrategy":"type-guard","validationCode":"if (candidate is DynamicMethod) candidate = WrapInFactory(candidate);","typeGuard":"static bool IsDirectDynamicMethod(MethodInfo m) => m is System.Reflection.Emit.DynamicMethod;","tryCatchPattern":"try { new Patch(m, ...); } catch (Exception e) when (e.Message.Contains(\"dynamic method\")) { /* use factory */ }","preventionTips":["Always register dynamic patch methods through factory MethodInfos","Never store bare DynamicMethod instances in patch lists"],"tags":["dynamic-method","reflection-emit","harmony","csharp"],"backgroundTag":"unsupported-operation","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"}