{"record":{"id":"ba34a81186016561","repo":"pardeike/Harmony","slug":"either-fielddeclaringtype-must-be-a-class-or-field-must-be","errorCode":null,"errorMessage":"Either FieldDeclaringType must be a class or field must be static","messagePattern":"Either FieldDeclaringType must be a class or field must be static","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":1374,"sourceCode":"\t\t///\r\n\t\tpublic static FieldRef<object, F> FieldRefAccess<F>(Type type, string fieldName)\r\n\t\t{\r\n\t\t\tif (type is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(type));\r\n\t\t\tif (fieldName is null)\r\n\t\t\t\tthrow new ArgumentNullException(nameof(fieldName));\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tvar fieldInfo = Field(type, fieldName);\r\n\t\t\t\tif (fieldInfo is null)\r\n\t\t\t\t\tthrow new MissingFieldException(type.Name, fieldName);\r\n\t\t\t\t// Backwards compatibility: This supports static fields, even those defined in structs. For static fields, T is effectively ignored.\r\n\t\t\t\tif (fieldInfo.IsStatic is false && 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(\"Either FieldDeclaringType must be a class or field must be static\");\r\n\t\t\t\t}\r\n\t\t\t\t// Field's declaring type cannot be object, since object has no fields, so always need a castclass for T=object.\r\n\t\t\t\treturn Tools.FieldRefAccess<object, F>(fieldInfo, needCastclass: true);\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(F)}> for {type}, {fieldName} caused an exception\", ex);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Creates a field reference delegate for an instance field of a class or static field (NOT an instance field of a struct)</summary>\r\n\t\t/// <typeparam name=\"F\"> type of the field</typeparam>\r\n\t\t/// <param name=\"typeColonName\">The member in the form <c>TypeFullName:MemberName</c>, where TypeFullName matches the form recognized by <a href=\"https://docs.microsoft.com/en-us/dotnet/api/system.type.gettype\">Type.GetType</a> like <c>Some.Namespace.Type</c>.</param>\r\n\t\t/// <returns>A readable/assignable <see cref=\"FieldRef{T,F}\"/> delegate with <c>T=object</c></returns>\r\n\t\t///\r\n\t\tpublic static FieldRef<object, F> FieldRefAccess<F>(string typeColonName)\r\n\t\t{\r\n\t\t\tvar info = Tools.TypColonName(typeColonName);\r","sourceCodeStart":1356,"sourceCodeEnd":1392,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L1356-L1392","documentation":"FieldRefAccess<F>(fieldInfo) throws this ArgumentException when given a non-static field whose declaring type is a value type. Ref-returning delegates cannot address fields inside structs, and the generic class constraint on T is not enough because callers may pass a FieldInfo from a struct. The check enforces: either the field is static, or its declaring type is a class.","triggerScenarios":"Calling AccessTools.FieldRefAccess<F>(fieldInfo) with a FieldInfo for an instance field declared on a struct (IsStatic == false, DeclaringType.IsValueType == true).","commonSituations":"Passing a struct's instance FieldInfo (e.g. a Vector3 field) into the delegate factory; generic helper code that collected FieldInfos from mixed class/struct types.","solutions":["Only pass static FieldInfos or FieldInfos declared on classes to FieldRefAccess<F>","For struct instance fields, fall back to FieldInfo.GetValue/SetValue with boxing","For static fields on structs, this overload still works (T is ignored for static fields)"],"exampleFix":"// before\nvar fi = AccessTools.Field(typeof(Vector3), \"x\");\nvar acc = AccessTools.FieldRefAccess<float>(fi); // throws\n// after\nvar fi = AccessTools.Field(typeof(Player), \"hp\");\nvar acc = AccessTools.FieldRefAccess<int>(fi);","handlingStrategy":"validation","validationCode":"if (fieldInfo.IsStatic is false && fieldInfo.DeclaringType is Type dt && dt.IsValueType)\n    throw new InvalidOperationException(\"Cannot create ref delegate for struct instance field\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check fieldInfo.DeclaringType.IsValueType before calling","Use FieldInfo.GetValue/SetValue for struct fields","Route static fields through StaticFieldRefAccess<F>(fieldInfo)"],"tags":["reflection","harmony","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"}