pardeike/Harmony · error · ArgumentException
T (FieldRefAccess instance type) must not be a value type
Error message
T (FieldRefAccess instance type) must not be a value type
What it means
FieldRefAccess<T,F>(type, fieldName) throws this ArgumentException when T (the instance type used to resolve the field) is a value type, because ref-returning delegates cannot reference fields inside boxed structs. Harmony checks typeof(T).IsValueType before resolving the field and throws immediately. Only class instance types (or a type for static-field compatibility purposes) are supported here.
Solutions
- Change the type argument T to the class that declares/contains the field; structs are not supported
- If the field is static on a struct, use AccessTools.Field or StaticFieldRefAccess<F>(fieldInfo) instead
- Use the explicit FieldInfo overload FieldRefAccess<F>(fieldInfo) only for static fields or class-declared instance fields
Example fix
// before
var accessor = AccessTools.FieldRefAccess<MyStruct, int>("counter");
// after
var accessor = AccessTools.StaticFieldRefAccess<int>(AccessTools.Field(typeof(MyStruct), "counter")); Defensive patterns
Strategy: validation
Validate before calling
if (typeof(T).IsValueType) throw new InvalidOperationException("FieldRefAccess requires a class instance type");
var f = AccessTools.Field(typeof(T), fieldName) ?? throw new MissingFieldException(typeof(T).Name, fieldName); Prevention
- Never bind T to a struct; keep the class constraint in mind
- Use StaticFieldRefAccess for static fields, including statics on structs
- Verify field existence and type before building ref delegates
When it happens
Trigger: Calling AccessTools.FieldRefAccess<T, F>(type, fieldName) with T bound to a struct, e.g. FieldRefAccess<MyStruct, int>(...), even if the fieldName identifies a static field.
Common situations: Trying to build a fast field-ref delegate for a struct field in a game mod; generic type inference binding T to a value type like Vector3, int, or an enum.
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
- Either FieldDeclaringType must be a class or field must be…
- FieldDeclaringType must be a class
- FieldRefAccess< , > for caused an exception
- FieldRefAccess< , > for , caused an exception
- FieldRefAccess< > for , caused an exception
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/37f14dad752836cc.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:1282
/// </typeparam>
/// <param name="fieldName">The name of the field</param>
/// <returns>A readable/assignable <see cref="FieldRef{T,F}"/> delegate</returns>
/// <remarks>
/// <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 FieldRef<T, F> FieldRefAccess<T, F>(string fieldName)
{
if (fieldName is null)
throw new ArgumentNullException(nameof(fieldName));
try
{
var delegateInstanceType = typeof(T);
if (delegateInstanceType.IsValueType)
throw new ArgumentException("T (FieldRefAccess instance type) must not be a value type");
return Tools.FieldRefAccess<T, F>(Tools.GetInstanceField(delegateInstanceType, fieldName), needCastclass: false);
}
catch (Exception ex)
{
throw new ArgumentException($"FieldRefAccess<{typeof(T)}, {typeof(F)}> for {fieldName} caused an exception", ex);
}
}
/// <summary>Creates an instance field reference for a specific instance of a class</summary>
/// <typeparam name="T">The class that defines the instance field, or derived class of this type</typeparam>
/// <typeparam name="F">
/// The type of the field; or if the field's type is a reference type (a class or interface, NOT a struct or other value type),
/// a type that <see cref="Type.IsAssignableFrom(Type)">is assignable from</see> that type; or if the field's type is an enum type,
/// either that type or the underlying integral type of that enum type
/// </typeparam>
/// <param name="instance">The instance</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>A readable/assignable reference to the field</returns>
View on GitHub (pinned to e7872dc170)