pardeike/Harmony · error · ArgumentException

Field must be static

Error message

Field must be static

What it means

Tools.StaticFieldRefAccess<F> builds a delegate that returns a ref to a static field, which requires the FieldInfo to have IsStatic set. If a non-static (instance) field is passed, ArgumentException("Field must be static") is thrown because there is no instance context in which the ref could be meaningful.

Solutions

  1. Pass a static field: resolve with BindingFlags.Static, e.g. typeof(T).GetField("name", BindingFlags.NonPublic | BindingFlags.Static)
  2. Verify fieldInfo.IsStatic before calling and route instance fields to the instance FieldRefAccess<T,F> overload (which takes the instance/ref target)
  3. If the field became instance-only after a target update, switch to the instance-based ref API

Example fix

// before
var f = typeof(C).GetField("counter", BindingFlags.NonPublic | BindingFlags.Instance);
var rf = Tools.StaticFieldRefAccess<int>(f); // instance field => throws
// after
var f = typeof(C).GetField("counter", BindingFlags.NonPublic | BindingFlags.Static);
var rf = Tools.StaticFieldRefAccess<int>(f);
Defensive patterns

Strategy: validation

Validate before calling

var f = typeof(T).GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
if (f is null || !f.IsStatic) throw new InvalidOperationException($"'{name}' is not a static field");

Type guard

static bool IsStaticField(FieldInfo f) => f?.IsStatic == true;

Try / catch

try { rf = Tools.StaticFieldRefAccess<int>(f); }
catch (ArgumentException) { /* f is an instance field: use FieldRefAccess<T, int> with the instance */ }

Prevention

When it happens

Trigger: Calling StaticFieldRefAccess<F>(fieldInfo) with an instance field's FieldInfo — commonly obtained via GetField with only NonPublic|Instance bindings, or from iterating DeclaringType.GetFields without filtering IsStatic.

Common situations: Developer picked the instance-field overload of a same-named field pair; the field was changed from static to instance (or vice versa) in a target-library update; generic tooling that resolves fields by name without checking BindingFlags.

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

Appendix: source

Thrown at Harmony/Tools/Tools.cs:104

		internal static StructFieldRef<T, F> StructFieldRefAccess<T, F>(FieldInfo fieldInfo) where T : struct
		{
			ValidateFieldType<F>(fieldInfo);

			var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_struct_fi_{fieldInfo.Name}",
				typeof(F).MakeByRefType(), [typeof(T).MakeByRefType()]);

			var il = dm.GetILGenerator();
			il.Emit(OpCodes.Ldarg_0);
			il.Emit(OpCodes.Ldflda, fieldInfo);
			il.Emit(OpCodes.Ret);

			return dm.Generate().CreateDelegate<StructFieldRef<T, F>>();
		}

		internal static FieldRef<F> StaticFieldRefAccess<F>(FieldInfo fieldInfo)
		{
			if (fieldInfo.IsStatic is false)
				throw new ArgumentException("Field must be static");
			ValidateFieldType<F>(fieldInfo);

			var dm = new DynamicMethodDefinition($"__refget_{fieldInfo.DeclaringType?.Name ?? "null"}_static_fi_{fieldInfo.Name}",
				typeof(F).MakeByRefType(), []);

			var il = dm.GetILGenerator();
			il.Emit(OpCodes.Ldsflda, fieldInfo);
			il.Emit(OpCodes.Ret);

			return dm.Generate().CreateDelegate<FieldRef<F>>();
		}

		internal static FieldInfo GetInstanceField(Type type, string fieldName)
		{
			var fieldInfo = Field(type, fieldName);
			if (fieldInfo is null)
				throw new MissingFieldException(type.Name, fieldName);
			if (fieldInfo.IsStatic)

View on GitHub (pinned to e7872dc170)