pardeike/Harmony · error · ArgumentException

You can only patch implemented methods/constructors. Patch…

Error message

You can only patch implemented methods/constructors. Patch the declared method {declaredMember.FullDescription()} instead.

What it means

Harmony can only patch the method that actually declares a body. If the MethodInfo passed to Patch() is an abstract method, interface method, or otherwise non-implemented member, Harmony resolves the declaring definition and throws an ArgumentException telling you to patch that declared method instead.

Solutions

  1. Patch the concrete implemented method: look the method up on the concrete type that provides the body
  2. Use Harmony's GetDeclaredMember() result from the error message as the patch target
  3. If the target is abstract/interface-only, patch a concrete derived implementation or use a different hook point

Example fix

// before
var original = typeof(IPlayer).GetMethod("Update");
harmony.Patch(original, prefix: prefixMethod);
// after
var original = typeof(Player).GetMethod("Update"); // concrete implementing type
harmony.Patch(original, prefix: prefixMethod);
Defensive patterns

Strategy: validation

Validate before calling

if (original is not null && !original.IsDeclaredMember())
{
    var declared = original.GetDeclaredMember();
    original = declared as MethodInfo ?? throw new InvalidOperationException($"Patch the declared method {declared.FullDescription()} instead");
}

Try / catch

try { harmony.Patch(original, prefix: prefix); }
catch (ArgumentException ex) when (ex.Message.Contains("implemented methods/constructors")) { /* retarget to concrete type */ }

Prevention

When it happens

Trigger: Calling Patch() with a MethodInfo obtained from a derived type, a base-type reference, a MakeGenericMethod result, or an interface method where IsDeclaredMember() is false — e.g. patching typeof(IFoo).GetMethod("Bar") or patching an abstract base method.

Common situations: Developers patch methods accessed through interface types or generic base classes; patching virtual methods via a base-class MethodInfo obtained from an interface lookup; targets refactored from concrete to abstract in a library update.

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/7fa2a28432236f22. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Public/PatchProcessor.cs:177

		{
			lock (locker)
			{
				return HarmonySharedState.GetPatchedMethods();
			}
		}

		/// <summary>Applies all registered patches</summary>
		/// <returns>The generated replacement method</returns>
		///
		public MethodInfo Patch()
		{
			if (original is null)
				throw new NullReferenceException($"Null method for {instance.Id}");

			if (original.IsDeclaredMember() is false)
			{
				var declaredMember = original.GetDeclaredMember();
				throw new ArgumentException($"You can only patch implemented methods/constructors. Patch the declared method {declaredMember.FullDescription()} instead.");
			}

			lock (locker)
			{
				var patchInfo = HarmonySharedState.GetPatchInfo(original) ?? new PatchInfo();

				patchInfo.AddPrefixes(instance.Id, prefix);
				patchInfo.AddPostfixes(instance.Id, postfix);
				patchInfo.AddTranspilers(instance.Id, transpiler);
				patchInfo.AddFinalizers(instance.Id, finalizer);
				patchInfo.AddInnerPrefixes(instance.Id, innerprefix);
				patchInfo.AddInnerPostfixes(instance.Id, innerpostfix);

				var replacement = PatchFunctions.UpdateWrapper(original, patchInfo);
				HarmonySharedState.UpdatePatchInfo(original, replacement, patchInfo);
				return replacement;
			}
		}

View on GitHub (pinned to e7872dc170)