pardeike/Harmony · error · ArgumentException
Either FieldDeclaringType must be a class or field must be…
Error message
Either FieldDeclaringType must be a class or field must be static
What it means
FieldRefAccess<F>(fieldInfo) throws this ArgumentException when given a non-static field whose declaring type is a value type. Ref-returning delegates cannot address fields inside structs, and the generic class constraint on T is not enough because callers may pass a FieldInfo from a struct. The check enforces: either the field is static, or its declaring type is a class.
Solutions
- Only pass static FieldInfos or FieldInfos declared on classes to FieldRefAccess<F>
- For struct instance fields, fall back to FieldInfo.GetValue/SetValue with boxing
- For static fields on structs, this overload still works (T is ignored for static fields)
Example fix
// before var fi = AccessTools.Field(typeof(Vector3), "x"); var acc = AccessTools.FieldRefAccess<float>(fi); // throws // after var fi = AccessTools.Field(typeof(Player), "hp"); var acc = AccessTools.FieldRefAccess<int>(fi);
Defensive patterns
Strategy: validation
Validate before calling
if (fieldInfo.IsStatic is false && fieldInfo.DeclaringType is Type dt && dt.IsValueType)
throw new InvalidOperationException("Cannot create ref delegate for struct instance field"); Prevention
- Check fieldInfo.DeclaringType.IsValueType before calling
- Use FieldInfo.GetValue/SetValue for struct fields
- Route static fields through StaticFieldRefAccess<F>(fieldInfo)
When it happens
Trigger: Calling AccessTools.FieldRefAccess<F>(fieldInfo) with a FieldInfo for an instance field declared on a struct (IsStatic == false, DeclaringType.IsValueType == true).
Common situations: Passing a struct's instance FieldInfo (e.g. a Vector3 field) into the delegate factory; generic helper code that collected FieldInfos from mixed class/struct types.
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
- 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/ba34a81186016561.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:1374
///
public static FieldRef<object, F> FieldRefAccess<F>(Type type, string fieldName)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
if (fieldName is null)
throw new ArgumentNullException(nameof(fieldName));
try
{
var fieldInfo = Field(type, fieldName);
if (fieldInfo is null)
throw new MissingFieldException(type.Name, fieldName);
// Backwards compatibility: This supports static fields, even those defined in structs. For static fields, T is effectively ignored.
if (fieldInfo.IsStatic is false && 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("Either FieldDeclaringType must be a class or field must be static");
}
// Field's declaring type cannot be object, since object has no fields, so always need a castclass for T=object.
return Tools.FieldRefAccess<object, F>(fieldInfo, needCastclass: true);
}
catch (Exception ex)
{
throw new ArgumentException($"FieldRefAccess<{typeof(F)}> for {type}, {fieldName} caused an exception", ex);
}
}
/// <summary>Creates a field reference delegate for an instance field of a class or static field (NOT an instance field of a struct)</summary>
/// <typeparam name="F"> type of the field</typeparam>
/// <param name="typeColonName">The member in the form <c>TypeFullName:MemberName</c>, where TypeFullName matches the form recognized by <a href="https://docs.microsoft.com/en-us/dotnet/api/system.type.gettype">Type.GetType</a> like <c>Some.Namespace.Type</c>.</param>
/// <returns>A readable/assignable <see cref="FieldRef{T,F}"/> delegate with <c>T=object</c></returns>
///
public static FieldRef<object, F> FieldRefAccess<F>(string typeColonName)
{
var info = Tools.TypColonName(typeColonName);
View on GitHub (pinned to e7872dc170)