pardeike/Harmony · error · Exception

Cannot directly reference dynamic method \

Error message

Cannot directly reference dynamic method \"{patch.FullDescription()}\" in Harmony. Use a factory method instead that will return the dynamic method.

What it means

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.

Solutions

  1. Pass a factory method instead: a MethodInfo returning DynamicMethod (with optional object[] parameter) so Harmony can re-create the method on demand
  2. If the method is truly static and long-lived, compile it to a normal method or use a static wrapper method as the patch
  3. Use PatchTools.GetPatchMethod / factory-style helper to register the dynamic method

Example fix

// before
DynamicMethod dm = EmitReplacement();
new Patch(dm, 0, null, -1, null, null, false);
// after
static DynamicMethod Factory() => EmitReplacement();
new Patch(typeof(Patches).GetMethod(nameof(Factory), BindingFlags.NonPublic | BindingFlags.Static), 0, null, -1, null, null, false);
Defensive patterns

Strategy: type-guard

Validate before calling

if (candidate is DynamicMethod) candidate = WrapInFactory(candidate);

Type guard

static bool IsDirectDynamicMethod(MethodInfo m) => m is System.Reflection.Emit.DynamicMethod;

Try / catch

try { new Patch(m, ...); } catch (Exception e) when (e.Message.Contains("dynamic method")) { /* use factory */ }

Prevention

When it happens

Trigger: Passing a System.Reflection.Emit.DynamicMethod to the Patch(MethodInfo, int, string, int, string[], string[], bool) constructor, directly or via PatchMethods/ManualPatchMethods lists.

Common situations: 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.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15). Data as JSON: /api/errors/523f7f3d7aab65c3. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Public/Patch.cs:83

			{
				patchMethod = value;
				methodToken = patchMethod.MetadataToken;
				moduleGUID = patchMethod.Module.ModuleVersionId.ToString();
			}
		}

		/// <summary>Creates a patch</summary>
		/// <param name="patch">The method of the patch</param>
		/// <param name="index">Zero-based index</param>
		/// <param name="owner">An owner (Harmony ID)</param>
		/// <param name="priority">The priority, see <see cref="Priority"/></param>
		/// <param name="before">A list of Harmony IDs for patches that should run after this patch</param>
		/// <param name="after">A list of Harmony IDs for patches that should run before this patch</param>
		/// <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>
		///
		public Patch(MethodInfo patch, int index, string owner, int priority, string[] before, string[] after, bool debug)
		{
			if (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.");

			this.index = index;
			this.owner = owner;
			this.priority = priority == -1 ? Priority.Normal : priority;
			this.before = before ?? [];
			this.after = after ?? [];
			this.debug = debug;
			PatchMethod = patch;
		}

		/// <summary>Creates a patch</summary>
		/// <param name="method">The method of the patch</param>
		/// <param name="index">Zero-based index</param>
		/// <param name="owner">An owner (Harmony ID)</param>
		public Patch(HarmonyMethod method, int index, string owner)
			: this(method.method, index, owner, method.priority, method.before, method.after, method.debug ?? false) { }

		internal int MethodToken => methodToken;

View on GitHub (pinned to e7872dc170)