pardeike/Harmony · error · ArgumentException

Patch method must be static

Error message

Patch method {patch.FullDescription()} must be static

What it means

Harmony patch methods (prefix, postfix, finalizer, transpiler) must be static; only ReversePatch stand-ins may be instance methods. AttributePatch.Create throws this ArgumentException when the annotated patch method's patch type is not ReversePatch and the method is not static.

Solutions

  1. Mark the patch method static (and keep parameters injected via Harmony's special names instead of instance state).
  2. If you need per-instance state, use static fields, __instance/__0 injection, or a state object parameter.
  3. If the method is genuinely a ReversePatch stand-in, ensure GetPatchType resolves it as ReversePatch (attribute/naming) so the static check is bypassed.

Example fix

// before
class P {
  [HarmonyPrefix]
  void Prefix(int x) { } // instance method
}

// after
class P {
  [HarmonyPrefix]
  static void Prefix(int x) { }
Defensive patterns

Strategy: validation

Validate before calling

foreach (var m in typeof(MyPatches).GetMethods())
    if (m.GetCustomAttributes(true).Any(a => a.GetType().Name.StartsWith("Harmony")) && !m.IsStatic)
        throw new InvalidOperationException($"{m.Name} must be static");

Type guard

static bool IsValidPatchMethod(MethodInfo m) =>
    m.IsStatic || m.GetCustomAttributes(true).Any(a => a is HarmonyAttribute h && h.info.patchType == HarmonyPatchType.ReversePatch);

Try / catch

try { harmony.PatchAll(typeof(MyPatches)); }
catch (ArgumentException ex) when (ex.Message.Contains("must be static"))
{ Logger.Error($"Non-static patch method: {ex.Message}"); }

Prevention

When it happens

Trigger: Annotating an instance method with [HarmonyPrefix]/[HarmonyPostfix]/[HarmonyFinalizer]/[HarmonyTranspiler] and running PatchAll/PatchClassProcessor over the class.

Common situations: Converting older patches to instance-style for state, accidentally attaching attributes to instance helpers, or moving a patch method into a class without keeping it static.

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


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

Appendix: source

Thrown at Harmony/Internal/PatchModels.cs:105

			HarmonyPatchType.InnerPostfix
		];

		internal HarmonyMethod info;
		internal HarmonyPatchType? type;

		internal static AttributePatch Create(MethodInfo patch)
		{
			if (patch is null)
				throw new NullReferenceException("Patch method cannot be null");

			var allAttributes = patch.GetCustomAttributes(true);
			var methodName = patch.Name;
			var type = GetPatchType(methodName, allAttributes);
			if (type is null)
				return null;

			if (type != HarmonyPatchType.ReversePatch && patch.IsStatic is false)
				throw new ArgumentException("Patch method " + patch.FullDescription() + " must be static");

			var list = allAttributes
				.Where(attr => attr.GetType().BaseType.FullName == PatchTools.harmonyAttributeFullName)
				.Select(attr =>
				{
					var f_info = AccessTools.Field(attr.GetType(), nameof(HarmonyAttribute.info));
					return f_info.GetValue(attr);
				})
				.Select(AccessTools.MakeDeepCopy<HarmonyMethod>)
				.ToList();
			var info = HarmonyMethod.Merge(list);
			info.method = patch;

			return new AttributePatch() { info = info, type = type };
		}

		static HarmonyPatchType? GetPatchType(string methodName, object[] allAttributes)
		{

View on GitHub (pinned to e7872dc170)