pardeike/Harmony · error · ArgumentException
FieldRefAccess< , > for caused an exception
Error message
FieldRefAccess<{typeof(T)}, {typeof(F)}> for {fieldInfo} caused an exception What it means
This is the catch-all wrapper in AccessTools.FieldRefAccess<T,F>(FieldInfo): any exception thrown while building the field ref delegate (including error 80 and reflection failures) is rethrown as an ArgumentException whose message includes the types T, F and the FieldInfo, with the original exception as InnerException.
Solutions
- Inspect the InnerException to find the root cause
- Verify fieldInfo.IsStatic is false and fieldInfo.DeclaringType is a non-value class type
- Ensure typeof(F) matches the actual field type
- Pre-validate the field with AccessTools.FieldAccess or a DeclaringType.IsValueType check before calling
Example fix
// before
var rf = AccessTools.FieldRefAccess<T, F>(fi); // throws opaque message
// after
if (fi is null || fi.DeclaringType is { IsValueType: true })
return AccessTools.StructFieldRefAccess<T, F>(fi);
var rf = AccessTools.FieldRefAccess<T, F>(fi); Defensive patterns
Strategy: try-catch
Validate before calling
var fi = AccessTools.Field(typeof(T), fieldName);
bool ok = fi is { IsStatic: false } && fi.DeclaringType is { IsValueType: false };
Try / catch
try { var rf = AccessTools.FieldRefAccess<T, F>(fi); }
catch (ArgumentException ex) { throw new InvalidOperationException($"FieldRef failed for {typeof(T)}.{fi?.Name}", ex.InnerException ?? ex); } Prevention
- Always log InnerException when wrapping Harmony accessors
- Validate the FieldInfo (staticness, declaring type, field type) before building delegates
- Check target assembly versions after dependency updates
When it happens
Trigger: Any failure inside the FieldRefAccess<T,F>(fieldInfo) overload: struct declaring type with a non-static field, fieldInfo not being an instance/class field, or a failure in Tools.FieldRefAccess delegate emission (e.g. FieldRefNeedsClasscast edge cases).
Common situations: Runtime patch setup failing during mod/plugin startup; the real cause is in ex.InnerException, which is often missed when only the message is logged.
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
- FieldRefAccess< , > for , caused an exception
- 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…
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/7266ce48d4d624b4.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/AccessTools.cs:1448
var delegateInstanceType = typeof(T);
if (delegateInstanceType.IsValueType)
throw new ArgumentException("T (FieldRefAccess instance type) must not be a value type");
var needCastclass = false;
// Backwards compatibility: FieldRefAccess<F>(Type type, string fieldName) used to delegate to this method,
// and thus this method must support the same cases - namely, static fields. 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");
needCastclass = Tools.FieldRefNeedsClasscast(delegateInstanceType, declaringType);
}
return Tools.FieldRefAccess<T, F>(fieldInfo, needCastclass);
}
catch (Exception ex)
{
throw new ArgumentException($"FieldRefAccess<{typeof(T)}, {typeof(F)}> for {fieldInfo} caused an exception", ex);
}
}
/// <summary>Creates a field reference for an instance field of a class</summary>
/// <typeparam name="T">
/// The type that defines the field; or a parent class (including <see cref="object"/>), implemented interface, or derived class of this type
/// ("<c>instanceOfT is FieldDeclaringType</c>" must be possible)
/// </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="fieldInfo">The field</param>
/// <returns>A readable/assignable reference to the field</returns>
/// <remarks>
/// <para>
View on GitHub (pinned to e7872dc170)