pardeike/Harmony · error · ArgumentException

Field must not be static

Error message

Field must not be static

What it means

The instance FieldRefAccess overloads only support instance fields; a static field has no instance operand. Harmony throws this ArgumentException when fieldInfo.IsStatic is true to prevent silently wrong ref semantics.

Solutions

  1. Use the static field accessor: AccessTools.FieldRefAccess<F>(fieldInfo) / StaticFieldRefAccess overloads
  2. Re-check the target assembly version and the field's staticness
  3. Fix BindingFlags in the field lookup (remove Static or add Instance explicitly)
  4. Pre-validate with fieldInfo.IsStatic before choosing the overload

Example fix

// before
var rf = AccessTools.FieldRefAccess<MyClass, int>(staticFi); // static field
// after
var rf = AccessTools.StaticFieldRefAccess<MyClass, int>(staticFi);
Defensive patterns

Strategy: validation

Validate before calling

if (fieldInfo is { IsStatic: true }) return AccessTools.StaticFieldRefAccess<F>(fieldInfo); // branch instead of instance overload

Type guard

static bool IsInstanceField(FieldInfo fi) => fi is { IsStatic: false };

Prevention

When it happens

Trigger: Calling AccessTools.FieldRefAccess<T,F>(fieldInfo, instance) (or FieldRefAccess<T,F>(fieldInfo) with instance semantics) with a FieldInfo whose IsStatic is true, e.g. obtained via BindingFlags.FlattenHierarchy | Static.

Common situations: A field changed from instance to static in a target-library update; field lookup using broad BindingFlags that return the static field.

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

Appendix: source

Thrown at Harmony/Tools/AccessTools.cs:1489

		/// <para>
		/// For backwards compatibility, there is no class constraint on <typeparamref name="T"/>.
		/// Instead, the non-value-type check is done at runtime within the method.
		/// </para>
		/// </remarks>
		///
		public static ref F FieldRefAccess<T, F>(T instance, FieldInfo fieldInfo)
		{
			if (instance is null)
				throw new ArgumentNullException(nameof(instance));
			if (fieldInfo is null)
				throw new ArgumentNullException(nameof(fieldInfo));
			try
			{
				var delegateInstanceType = typeof(T);
				if (delegateInstanceType.IsValueType)
					throw new ArgumentException("T (FieldRefAccess instance type) must not be a value type");
				if (fieldInfo.IsStatic)
					throw new ArgumentException("Field must not be static");
				var needCastclass = false;
				if (fieldInfo.DeclaringType is Type declaringType)
				{
					// When fieldInfo is passed to FieldRefAccess methods, the T generic class constraint is insufficient to ensure that
					// the field is not a struct instance field, since T could be object, ValueType, or an interface that the struct implements.
					if (declaringType.IsValueType)
						throw new ArgumentException("FieldDeclaringType must be a class");
					needCastclass = Tools.FieldRefNeedsClasscast(delegateInstanceType, declaringType);
				}
				return ref Tools.FieldRefAccess<T, F>(fieldInfo, needCastclass)(instance);
			}
			catch (Exception ex)
			{
				throw new ArgumentException($"FieldRefAccess<{typeof(T)}, {typeof(F)}> for {instance}, {fieldInfo} caused an exception", ex);
			}
		}

		/// <summary>A readable/assignable reference delegate to an instance field of a struct</summary>

View on GitHub (pinned to e7872dc170)