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
- Mark the patch method static (and keep parameters injected via Harmony's special names instead of instance state).
- If you need per-instance state, use static fields, __instance/__0 injection, or a state object parameter.
- 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
- Always declare prefix/postfix/finalizer/transpiler methods static.
- Use injected parameters (__instance, __0, __result) instead of instance fields for state.
- Add a startup reflection check that all [Harmony*]-annotated methods in your assembly are static.
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
- Patch method cannot be null
- The type must declare an empty constructor (the constructor…
- position( ) + count( ) > buffer.Length( )
- Unbalanced exception markers – cannot rewrite.
- Multiple loaded HarmonySharedState types prevent safe…
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)