{"record":{"id":"75b1d293f8107f58","repo":"pardeike/Harmony","slug":"fielddeclaringtype-must-be-t-structfieldrefaccess-instance","errorCode":null,"errorMessage":"FieldDeclaringType must be T (StructFieldRefAccess instance type)","messagePattern":"FieldDeclaringType must be T \\(StructFieldRefAccess instance type\\)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/Tools.cs","lineNumber":145,"sourceCode":"\t\tinternal static bool FieldRefNeedsClasscast(Type delegateInstanceType, Type declaringType)\r\n\t\t{\r\n\t\t\tvar needCastclass = false;\r\n\t\t\tif (delegateInstanceType != declaringType)\r\n\t\t\t{\r\n\t\t\t\tneedCastclass = delegateInstanceType.IsAssignableFrom(declaringType);\r\n\t\t\t\tif (needCastclass is false && declaringType.IsAssignableFrom(delegateInstanceType) is false)\r\n\t\t\t\t\tthrow new ArgumentException(\"FieldDeclaringType must be assignable from or to T (FieldRefAccess instance type) - \" +\r\n\t\t\t\t\t\t\"\\\"instanceOfT is FieldDeclaringType\\\" must be possible\");\r\n\t\t\t}\r\n\t\t\treturn needCastclass;\r\n\t\t}\r\n\r\n\t\tinternal static void ValidateStructField<T, F>(FieldInfo fieldInfo) where T : struct\r\n\t\t{\r\n\t\t\tif (fieldInfo.IsStatic)\r\n\t\t\t\tthrow new ArgumentException(\"Field must not be static\");\r\n\t\t\tif (fieldInfo.DeclaringType != typeof(T))\r\n\t\t\t\tthrow new ArgumentException(\"FieldDeclaringType must be T (StructFieldRefAccess instance type)\");\r\n\t\t}\r\n\t}\r\n}\r\n","sourceCodeStart":127,"sourceCodeEnd":149,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/Tools.cs#L127-L149","documentation":"ValidateStructField requires the field's declaring type to exactly equal the struct type T used for StructFieldRefAccess, because the instance you pass is a T and the emitted ref IL assumes the field lives on T. A field inherited from a base class (DeclaringType != T) would compute the wrong address.","triggerScenarios":"Calling StructFieldRefAccess<T, F> with a field whose DeclaringType is a base class (or different struct) than the struct type argument T.","commonSituations":"Accessing a field inherited from a parent class through the derived struct/class type; resolving the field via typeof(Base) but using T = derived.","solutions":["Resolve the field on its actual declaring type and pass that type as T","Use the class-based FieldRefAccess overload when the field is inherited rather than declared on the struct","Check fieldInfo.DeclaringType == typeof(T) before calling"],"exampleFix":"// before\nvar fr = Tools.StructFieldRefAccess<Derived, int>(AccessTools.Field(typeof(Base), \"hp\"));\n// after\nvar fr = Tools.FieldRefAccess<Derived, int>(AccessTools.Field(typeof(Base), \"hp\"));","handlingStrategy":"validation","validationCode":"var fi = AccessTools.Field(typeof(T), fieldName);\nif (fi != null && fi.DeclaringType != typeof(T)) throw new ArgumentException($\"{fieldName} is declared on {fi.DeclaringType}, not {typeof(T)}\");","typeGuard":"bool DeclaredOnStruct<T>(FieldInfo fi) where T : struct => fi?.DeclaringType == typeof(T);","tryCatchPattern":"try { var fr = Tools.StructFieldRefAccess<T, F>(fi); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"FieldDeclaringType must be T\")) { /* switch to FieldRefAccess with declaring type */ }","preventionTips":["Match T exactly to fieldInfo.DeclaringType for struct refs","For inherited fields use class FieldRefAccess instead of the struct variant","Resolve fields with AccessTools.DeclaredField to get the exact declaring type"],"tags":["csharp","reflection","struct"],"backgroundTag":"type-mismatch","analyzedSha":"e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c","analyzedAt":"2026-09-15T22:47:11.550Z","contentChangedAt":"2026-09-15T22:47:11.550Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}