pardeike/Harmony · error · ArgumentException
FieldDeclaringType must be assignable from or to T…
Error message
FieldDeclaringType must be assignable from or to T (FieldRefAccess instance type) - "instanceOfT is FieldDeclaringType" must be possible
What it means
FieldRefNeedsClasscast validates that the generic instance type T used with FieldRefAccess can be cast to the field's declaring type (or vice versa). If neither type is assignable to the other, a field ref cannot be formed, so Harmony throws rather than emit invalid castclass IL.
Solutions
- Pass T as the field's actual declaring type (or a type in its inheritance chain)
- Resolve the fieldInfo with AccessTools.Field(typeof(DeclaringType), "name") so declaringType matches T
- Check fieldInfo.DeclaringType and your T for mutual assignability before calling
Example fix
// before var acc = Tools.FieldRefAccess<Base, int>(AccessTools.Field(typeof(Unrelated), "hp")); // after var acc = Tools.FieldRefAccess<Base, int>(AccessTools.Field(typeof(Base), "hp"));
Defensive patterns
Strategy: validation
Validate before calling
var fi = AccessTools.Field(typeof(Declaring), name);
if (fi == null || !(typeof(T).IsAssignableFrom(fi.DeclaringType) || fi.DeclaringType.IsAssignableFrom(typeof(T))))
throw new ArgumentException("T and field declaring type are not assignable"); Type guard
bool FieldRefCompatible<T>(FieldInfo fi) =>
fi != null && (typeof(T).IsAssignableFrom(fi.DeclaringType) || fi.DeclaringType.IsAssignableFrom(typeof(T))); Try / catch
try { var acc = Tools.FieldRefAccess<T, F>(fi); }
catch (ArgumentException ex) when (ex.Message.StartsWith("FieldDeclaringType must be assignable")) { /* use declaring type as T */ } Prevention
- Always pass the field's DeclaringType as T for FieldRefAccess
- Resolve the FieldInfo from typeof(T) itself, not an unrelated type
- Check mutual assignability of T and fieldInfo.DeclaringType before forming refs
When it happens
Trigger: Calling FieldRefAccess<T, F>(fieldInfo) where T (delegateInstanceType) and the field's declaringType are unrelated types (neither IsAssignableFrom the other).
Common situations: Using a derived wrapper class as T while the field is declared on a base class the wrapper doesn't inherit; mixing structs/classes; target library refactor moved the field to an unrelated type.
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
- Ambiguous match for HarmonyMethod
- No method found for type=
- No method found for , parameters= , generics=
- No field found for and
- Cannot not find method for type
AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15).
Data as JSON: /api/errors/ffd9a40a4e53c0ef.
Report an issue: GitHub.
Appendix: source
Thrown at Harmony/Tools/Tools.cs:134
internal static FieldInfo GetInstanceField(Type type, string fieldName)
{
var fieldInfo = Field(type, fieldName);
if (fieldInfo is null)
throw new MissingFieldException(type.Name, fieldName);
if (fieldInfo.IsStatic)
throw new ArgumentException("Field must not be static");
return fieldInfo;
}
internal static bool FieldRefNeedsClasscast(Type delegateInstanceType, Type declaringType)
{
var needCastclass = false;
if (delegateInstanceType != declaringType)
{
needCastclass = delegateInstanceType.IsAssignableFrom(declaringType);
if (needCastclass is false && declaringType.IsAssignableFrom(delegateInstanceType) is false)
throw new ArgumentException("FieldDeclaringType must be assignable from or to T (FieldRefAccess instance type) - " +
"\"instanceOfT is FieldDeclaringType\" must be possible");
}
return needCastclass;
}
internal static void ValidateStructField<T, F>(FieldInfo fieldInfo) where T : struct
{
if (fieldInfo.IsStatic)
throw new ArgumentException("Field must not be static");
if (fieldInfo.DeclaringType != typeof(T))
throw new ArgumentException("FieldDeclaringType must be T (StructFieldRefAccess instance type)");
}
}
}
View on GitHub (pinned to e7872dc170)