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
- Use the static field accessor: AccessTools.FieldRefAccess<F>(fieldInfo) / StaticFieldRefAccess overloads
- Re-check the target assembly version and the field's staticness
- Fix BindingFlags in the field lookup (remove Static or add Instance explicitly)
- 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
- Use precise BindingFlags (Instance for instance fields, Static for static)
- Re-check field staticness when upgrading the patched library
- Branch static vs instance accessors at delegate-creation time
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
- T (FieldRefAccess instance type) must not be a value type
- FieldRefAccess< , > for caused an exception
- FieldRefAccess< , > for , caused an exception
- Either FieldDeclaringType must be a class or field must be…
- FieldRefAccess< > for , caused an exception
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)