{"record":{"id":"30522477f10306e8","repo":"pardeike/Harmony","slug":"fielddeclaringtype-must-be-a-class","errorCode":null,"errorMessage":"FieldDeclaringType must be a class","messagePattern":"FieldDeclaringType must be a class","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":1496,"sourceCode":"\t\t{\r\n\t\t\tif (instance is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(instance));\r\n\t\t\tif (fieldInfo is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(fieldInfo));\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tvar delegateInstanceType = typeof(T);\r\n\t\t\t\tif (delegateInstanceType.IsValueType)\r\n\t\t\t\t\tthrow new ArgumentException(\"T (FieldRefAccess instance type) must not be a value type\");\r\n\t\t\t\tif (fieldInfo.IsStatic)\r\n\t\t\t\t\tthrow new ArgumentException(\"Field must not be static\");\r\n\t\t\t\tvar needCastclass = false;\r\n\t\t\t\tif (fieldInfo.DeclaringType is Type declaringType)\r\n\t\t\t\t{\r\n\t\t\t\t\t// When fieldInfo is passed to FieldRefAccess methods, the T generic class constraint is insufficient to ensure that\r\n\t\t\t\t\t// the field is not a struct instance field, since T could be object, ValueType, or an interface that the struct implements.\r\n\t\t\t\t\tif (declaringType.IsValueType)\r\n\t\t\t\t\t\tthrow new ArgumentException(\"FieldDeclaringType must be a class\");\r\n\t\t\t\t\tneedCastclass = Tools.FieldRefNeedsClasscast(delegateInstanceType, declaringType);\r\n\t\t\t\t}\r\n\t\t\t\treturn ref Tools.FieldRefAccess<T, F>(fieldInfo, needCastclass)(instance);\r\n\t\t\t}\r\n\t\t\tcatch (Exception ex)\r\n\t\t\t{\r\n\t\t\t\tthrow new ArgumentException($\"FieldRefAccess<{typeof(T)}, {typeof(F)}> for {instance}, {fieldInfo} caused an exception\", ex);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>A readable/assignable reference delegate to an instance field of a struct</summary>\r\n\t\t/// <typeparam name=\"T\">The struct that defines the instance field</typeparam>\r\n\t\t/// <typeparam name=\"F\">\r\n\t\t/// 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),\r\n\t\t/// a type that <see cref=\"Type.IsAssignableFrom(Type)\">is assignable from</see> that type; or if the field's type is an enum type,\r\n\t\t/// either that type or the underlying integral type of that enum type\r\n\t\t/// </typeparam>\r\n\t\t/// <param name=\"instance\">A reference to the runtime instance to access the field</param>\r","sourceCodeStart":1478,"sourceCodeEnd":1514,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L1478-L1514","documentation":"AccessTools.FieldRefAccess<T,F>(fieldInfo, instance) requires the field's DeclaringType to be a class because the emitted ref delegate uses ldflda on a typed reference. Instance fields on structs are unsupported here (use StructFieldRefAccess).","triggerScenarios":"fieldInfo is a non-static instance field whose DeclaringType.IsValueType is true, passed to the class-instance FieldRefAccess overload.","commonSituations":"Fields discovered via typeof(object) or interface lookup that actually resolve to struct implementations; generic reflection helpers that process both classes and structs uniformly.","solutions":["Switch to AccessTools.StructFieldRefAccess<T,F> when the declaring type is a struct","Verify fieldInfo.DeclaringType is the class you expect, not a struct","If fieldInfo was found via a derived/interface type, re-find it on the concrete class","Pre-check declaringType.IsValueType before selecting the accessor"],"exampleFix":"// before\nvar rf = AccessTools.FieldRefAccess<MyClass, int>(structFieldInfo);\n// after\nvar rf = AccessTools.StructFieldRefAccess<MyStruct, int>(structFieldInfo);","handlingStrategy":"validation","validationCode":"if (fieldInfo.DeclaringType is { IsValueType: true }) return AccessTools.StructFieldRefAccess<T, F>(fieldInfo);","typeGuard":"static bool IsClassDeclaredInstanceField(FieldInfo fi) => fi is { IsStatic: false } && fi.DeclaringType is { IsValueType: false };","tryCatchPattern":null,"preventionTips":["Never re-find fields via typeof(object) or interfaces when the concrete type is a struct","Assert DeclaringType is a class before class-based FieldRefAccess","Test accessors against the exact patched types"],"tags":["harmony","reflection","valuetype","fieldref"],"backgroundTag":"invalid-argument-value","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"}