{"record":{"id":"7266ce48d4d624b4","repo":"pardeike/Harmony","slug":"fieldrefaccess-typeof-t-typeof-f-for-fieldinfo-caused-an","errorCode":null,"errorMessage":"FieldRefAccess<{typeof(T)}, {typeof(F)}> for {fieldInfo} caused an exception","messagePattern":"FieldRefAccess<(.+?), (.+?)> for (.+?) caused an exception","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Tools/AccessTools.cs","lineNumber":1448,"sourceCode":"\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\tvar needCastclass = false;\r\n\t\t\t\t// Backwards compatibility: FieldRefAccess<F>(Type type, string fieldName) used to delegate to this method,\r\n\t\t\t\t// and thus this method must support the same cases - namely, static fields. 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\tneedCastclass = Tools.FieldRefNeedsClasscast(delegateInstanceType, declaringType);\r\n\t\t\t\t}\r\n\t\t\t\treturn Tools.FieldRefAccess<T, F>(fieldInfo, needCastclass);\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 {fieldInfo} caused an exception\", ex);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>Creates a field reference for an instance field of a class</summary>\r\n\t\t/// <typeparam name=\"T\">\r\n\t\t/// The type that defines the field; or a parent class (including <see cref=\"object\"/>), implemented interface, or derived class of this type\r\n\t\t/// (\"<c>instanceOfT is FieldDeclaringType</c>\" must be possible)\r\n\t\t/// </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\">The instance</param>\r\n\t\t/// <param name=\"fieldInfo\">The field</param>\r\n\t\t/// <returns>A readable/assignable reference to the field</returns>\r\n\t\t/// <remarks>\r\n\t\t/// <para>\r","sourceCodeStart":1430,"sourceCodeEnd":1466,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Tools/AccessTools.cs#L1430-L1466","documentation":"This is the catch-all wrapper in AccessTools.FieldRefAccess<T,F>(FieldInfo): any exception thrown while building the field ref delegate (including error 80 and reflection failures) is rethrown as an ArgumentException whose message includes the types T, F and the FieldInfo, with the original exception as InnerException.","triggerScenarios":"Any failure inside the FieldRefAccess<T,F>(fieldInfo) overload: struct declaring type with a non-static field, fieldInfo not being an instance/class field, or a failure in Tools.FieldRefAccess delegate emission (e.g. FieldRefNeedsClasscast edge cases).","commonSituations":"Runtime patch setup failing during mod/plugin startup; the real cause is in ex.InnerException, which is often missed when only the message is logged.","solutions":["Inspect the InnerException to find the root cause","Verify fieldInfo.IsStatic is false and fieldInfo.DeclaringType is a non-value class type","Ensure typeof(F) matches the actual field type","Pre-validate the field with AccessTools.FieldAccess or a DeclaringType.IsValueType check before calling"],"exampleFix":"// before\nvar rf = AccessTools.FieldRefAccess<T, F>(fi); // throws opaque message\n// after\nif (fi is null || fi.DeclaringType is { IsValueType: true })\n    return AccessTools.StructFieldRefAccess<T, F>(fi);\nvar rf = AccessTools.FieldRefAccess<T, F>(fi);","handlingStrategy":"try-catch","validationCode":"var fi = AccessTools.Field(typeof(T), fieldName);\nbool ok = fi is { IsStatic: false } && fi.DeclaringType is { IsValueType: false };\n","typeGuard":null,"tryCatchPattern":"try { var rf = AccessTools.FieldRefAccess<T, F>(fi); }\ncatch (ArgumentException ex) { throw new InvalidOperationException($\"FieldRef failed for {typeof(T)}.{fi?.Name}\", ex.InnerException ?? ex); }","preventionTips":["Always log InnerException when wrapping Harmony accessors","Validate the FieldInfo (staticness, declaring type, field type) before building delegates","Check target assembly versions after dependency updates"],"tags":["harmony","reflection","fieldref","wrapper"],"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"}