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
- Set F to the exact declared field type, which always works: FieldRefAccess<T, FieldType>(field)
- 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)
- 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
- Remember the direction: F must be a base/interface OF the field type, not a derived type
- Default to F = exact declared field type; only widen deliberately
- Re-verify assignability after target-library updates that change field declared types
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
- FieldRefAccess return type must be the same as FieldType or…
- FieldRefAccess return type must be the same as FieldType…
- Field must be static
- __state type mismatch in patch
- Return type of pass through postfix
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)