pardeike/Harmony · error · ArgumentException

Member must be of type EventInfo, FieldInfo, MethodInfo, or…

Error message

Member must be of type EventInfo, FieldInfo, MethodInfo, or PropertyInfo

What it means

AccessTools.GetUnderlyingType throws this ArgumentException when the MemberInfo passed to it is not an EventInfo, FieldInfo, MethodInfo, or PropertyInfo (e.g. it is a Type, ConstructorInfo, or nested type). The switch over MemberTypes has no matching case for such members, so Harmony throws to signal an unusable input. The method only computes 'the type of data a member holds/returns', which does not exist for other member kinds.

Solutions

  1. Verify member.MemberType is one of MemberTypes.Event/Field/Method/Property before calling GetUnderlyingType
  2. Filter collections with .Where(m => m is not ConstructorInfo and not Type) before processing
  3. Use member.GetType()-specific APIs (e.g. ConstructorInfo has no return type) for other member kinds

Example fix

// before
var t = AccessTools.GetUnderlyingType(AccessTools.Constructor(typeof(Foo)));
// after
MemberInfo mi = AccessTools.Method(typeof(Foo), "Bar");
var t = mi is ConstructorInfo ? null : AccessTools.GetUnderlyingType(mi);
Defensive patterns

Strategy: type-guard

Validate before calling

if (member is not (EventInfo or FieldInfo or MethodInfo or PropertyInfo)) throw new ArgumentException($"Unsupported member: {member?.GetType().Name}");

Type guard

bool SupportsGetUnderlyingType(MemberInfo m) => m is EventInfo or FieldInfo or MethodInfo or PropertyInfo;

Prevention

When it happens

Trigger: Calling AccessTools.GetUnderlyingType with a ConstructorInfo (e.g. obtained from AccessTools.Constructor), a Type object, a nested type, or any MemberInfo whose MemberType is not Event/Field/Method/Property.

Common situations: Passing the result of AccessTools.Constructor or AccessTools.TypeByName into GetUnderlyingType, or generically processing MemberInfo collections that include constructors and nested types.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of pardeike/Harmony@e7872dc170 (2026-09-15). Data as JSON: /api/errors/3fa6b048f241b522. Report an issue: GitHub.

Appendix: source

Thrown at Harmony/Tools/AccessTools.cs:912

				FileLog.Debug("AccessTools.GetPropertyNames: instance is null");
				return [];
			}
			return GetPropertyNames(instance.GetType());
		}

		/// <summary>Gets the type of any class member of</summary>
		/// <param name="member">A member</param>
		/// <returns>The class/type of this member</returns>
		///
		public static Type GetUnderlyingType(this MemberInfo member)
		{
			return member.MemberType switch
			{
				MemberTypes.Event => ((EventInfo)member).EventHandlerType,
				MemberTypes.Field => ((FieldInfo)member).FieldType,
				MemberTypes.Method => ((MethodInfo)member).ReturnType,
				MemberTypes.Property => ((PropertyInfo)member).PropertyType,
				_ => throw new ArgumentException("Member must be of type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"),
			};
		}

		/// <summary>Returns a <see cref="MethodInfo"/> by searching for module-id and token</summary>
		/// <param name="moduleGUID">The module of the method</param>
		/// <param name="token">The token of the method</param>
		/// <returns></returns>
		public static MethodInfo GetMethodByModuleAndToken(string moduleGUID, int token)
		{
#if NET5_0_OR_GREATER
			Module module = null;
			var assemblies = AppDomain.CurrentDomain.GetAssemblies();
			var moduleVersionGUID = new Guid(moduleGUID);
			Parallel.ForEach(assemblies, (assembly) =>
			{
				var allModules = assembly.GetModules();
				for (var i = 0; i < allModules.Length; i++)
					if (allModules[i].ModuleVersionId == moduleVersionGUID)

View on GitHub (pinned to e7872dc170)