pardeike/Harmony · error · ArgumentException

FieldRefAccess< , > for , caused an exception

Error message

FieldRefAccess<{typeof(T)}, {typeof(F)}> for {instance}, {fieldInfo} caused an exception

What it means

Catch-all wrapper in the instance-taking AccessTools.FieldRefAccess<T,F>(fieldInfo, instance): any failure while creating or invoking the ref delegate is rethrown as an ArgumentException naming T, F, the instance and the FieldInfo, with the real exception as InnerException.

Solutions

  1. Log and inspect ex.InnerException for the root cause
  2. Ensure instance is a live (non-null) object of type T or derived
  3. Confirm fieldInfo is a non-static instance field on a class
  4. Pre-validate T/fieldInfo/instance before calling (IsValueType, IsStatic, DeclaringType checks)

Example fix

// before
var rf = AccessTools.FieldRefAccess<T, F>(fi);
ref var v = ref rf(instance);
// after
if (fi.DeclaringType is { IsValueType: true }) throw new NotSupportedException("use StructFieldRefAccess");
var rf = AccessTools.FieldRefAccess<T, F>(fi);
ref var v = ref rf(instance ?? throw new InvalidOperationException("instance is null"));
Defensive patterns

Strategy: try-catch

Validate before calling

if (instance is null) throw new ArgumentNullException(nameof(instance));
if (typeof(T).IsValueType || fieldInfo is { IsStatic: true } || fieldInfo.DeclaringType is { IsValueType: true }) throw new ArgumentException("invalid FieldRefAccess parameters");

Type guard

static bool IsValidClassFieldRef<T, F>(FieldInfo fi) => !typeof(T).IsValueType && fi is { IsStatic: false } && fi.DeclaringType is { IsValueType: false };

Try / catch

try { var rf = AccessTools.FieldRefAccess<T, F>(fi); return rf(instance); }
catch (ArgumentException ex) { throw new InvalidOperationException("FieldRefAccess failed", ex.InnerException ?? ex); }

Prevention

When it happens

Trigger: Any of: T is a value type, field is static, field's declaring type is a struct, or the returned delegate fails when invoked with an incompatible/null instance.

Common situations: Passing a null or wrong-typed instance to the returned delegate; patch setup errors during startup where the inner exception is the actual diagnosis.

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/4a94349679ab713c. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Tools/AccessTools.cs:1503

				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>
		/// <typeparam name="T">The struct that defines the instance field</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">A reference to the runtime instance to access the field</param>
		/// <returns>A readable/assignable reference to the field</returns>
		///
		public delegate ref F StructFieldRef<T, F>(ref T instance) where T : struct;

		/// <summary>Creates a field reference delegate for an instance field of a struct</summary>
		/// <typeparam name="T">The struct that defines the instance field</typeparam>
		/// <typeparam name="F">

View on GitHub (pinned to e7872dc170)