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

  1. Inspect the InnerException to find the root cause
  2. Verify fieldInfo.IsStatic is false and fieldInfo.DeclaringType is a non-value class type
  3. Ensure typeof(F) matches the actual field type
  4. 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

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


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)