pardeike/Harmony · error · ArgumentException

FieldRefAccess return type must be assignable from…

Error message

FieldRefAccess return type must be assignable from FieldType for reference types

What it means

Tools.ValidateFieldType<F> for reference-type fields requires the FieldRefAccess delegate's return type F to be assignable FROM the field's type (returnType.IsAssignableFrom(fieldType)). Since the delegate returns a ref to the field, F must be the field type or a base/interface type of it; otherwise ArgumentException is thrown.

Solutions

  1. Set F to the exact declared field type, which always works: FieldRefAccess<T, FieldType>(field)
  2. If a base/interface view is needed, choose F as a type where F.IsAssignableFrom(field.FieldType) is true (base class or interface of the field type)
  3. Check statically before calling: typeof(F).IsAssignableFrom(fieldInfo.FieldType), and fall back to GetValue/SetValue if not

Example fix

// before
var rf = Tools.FieldRefAccess<C, FileStream>(typeof(C).GetField("stream")); // field is Stream
// after
var rf = Tools.FieldRefAccess<C, Stream>(typeof(C).GetField("stream"));
Defensive patterns

Strategy: validation

Validate before calling

if (!fieldInfo.FieldType.IsValueType && !typeof(F).IsAssignableFrom(fieldInfo.FieldType))
    throw new InvalidOperationException($"F={typeof(F)} is not assignable from field type {fieldInfo.FieldType}");

Type guard

static bool ValidRefTypeRef<F>(FieldInfo f) =>
    f.FieldType.IsValueType || typeof(F).IsAssignableFrom(f.FieldType);

Try / catch

try { rf = Tools.FieldRefAccess<T, F>(field); }
catch (ArgumentException) { rf = null; /* use fieldInfo.GetValue/SetValue instead */ }

Prevention

When it happens

Trigger: Calling FieldRefAccess<T,F> where the field is a reference type and F is unrelated or is a DERIVED type of the field type — e.g. field declared as object but F=string, or field declared as Stream but F=FileStream.

Common situations: Developers inverting the assignability direction (they can ref-read a derived field as its base, not vice versa); generic helpers that pick the wrong F; target library narrowing or widening a field's declared type across versions.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15). Data as JSON: /api/errors/ff4366f545b9da16. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Tools/Tools.cs:50

			var fieldType = fieldInfo.FieldType;
			if (returnType == fieldType)
				return;
			if (fieldType.IsEnum)
			{
				var underlyingType = Enum.GetUnderlyingType(fieldType);
				if (returnType != underlyingType)
					throw new ArgumentException("FieldRefAccess return type must be the same as FieldType or " +
						$"FieldType's underlying integral type ({underlyingType}) for enum types");
			}
			else if (fieldType.IsValueType)
			{
				// Boxing/unboxing is not allowed for ref values of value types.
				throw new ArgumentException("FieldRefAccess return type must be the same as FieldType for value types");
			}
			else
			{
				if (returnType.IsAssignableFrom(fieldType) is false)
					throw new ArgumentException("FieldRefAccess return type must be assignable from FieldType for reference types");
			}
		}

		internal static FieldRef<T, F> FieldRefAccess<T, F>(FieldInfo fieldInfo, bool needCastclass)
		{
			ValidateFieldType<F>(fieldInfo);
			var delegateInstanceType = typeof(T);
			var declaringType = fieldInfo.DeclaringType;

			var dm = new DynamicMethodDefinition($"__refget_{delegateInstanceType.Name}_fi_{fieldInfo.Name}",
				typeof(F).MakeByRefType(), [delegateInstanceType]);

			var il = dm.GetILGenerator();
			// Backwards compatibility: This supports static fields, even those defined in structs.
			if (fieldInfo.IsStatic)
			{
				// ldarg.0 + ldflda actually works for static fields, but the potential castclass (and InvalidCastException) below must be avoided
				// so might as well use the singular ldsflda for static fields.

View on GitHub (pinned to e7872dc170)