pardeike/Harmony · error · MissingMemberException

; available fields: ; available properties

Error message

{string.Join(",", names)}; available fields: {fields}; available properties: {properties}

What it means

AccessTools.ThrowMissingMemberException throws MissingMemberException listing the requested names plus all fields and properties actually available on the type. It is Harmony's helper for 'none of these members exist' situations, designed to make diagnosing typos and renamed members easy.

Solutions

  1. Read the message: pick the correct name from the listed available fields/properties.
  2. Re-resolve members after a target update by inspecting the type with AccessTools.GetFieldNames/GetPropertyNames.
  3. Search base types with AccessTools.GetFieldDescendants or flatten inherited members via AccessTools.GetDeclaredFields(type, includeBaseType: true).
  4. Guard resolution with AccessTools.Field/Property null checks before use.

Example fix

// before
AccessTools.Method(typeof(Player), "GetHelth");
// after
AccessTools.Method(typeof(Player), "GetHealth"); // message listed 'GetHealth' among available members
Defensive patterns

Strategy: validation

Validate before calling

var fi = AccessTools.Field(type, name) ?? AccessTools.Property(type, name);
if (fi is null)
{
    foreach (var candidate in AccessTools.GetFieldNames(type).Concat(AccessTools.GetPropertyNames(type)))
        Console.WriteLine(candidate); // pick correct spelling
}

Type guard

bool MemberExists(Type t, string n) => AccessTools.Field(t, n) is not null || AccessTools.Property(t, n) is not null;

Try / catch

try { AccessTools.X(type, names); }
catch (MissingMemberException ex) { /* parse 'available fields/properties' list from ex.Message to find correct name */ }

Prevention

When it happens

Trigger: Explicitly calling AccessTools.ThrowMissingMemberException(type, names), or indirectly via AccessTools APIs that resolve one of several member names (e.g. AccessTools.DeclaredMethod/Property variants taking name arrays) when no requested member exists on the type.

Common situations: Target library/game updated and renamed members; typos in member names; looking for a member on a base class where only the derived class has it; confusing a property with a field.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at Harmony/Tools/AccessTools.cs:2066

		public static bool IsNetFrameworkRuntime { get; } =
			Type.GetType("System.Runtime.InteropServices.RuntimeInformation", false)?.GetProperty("FrameworkDescription")
			.GetValue(null, null).ToString().StartsWith(".NET Framework") ?? IsMonoRuntime is false;

		/// <summary>True if the current runtime is .NET Core, false otherwise (Mono or .NET Framework)</summary>
		///
		public static bool IsNetCoreRuntime { get; } =
			Type.GetType("System.Runtime.InteropServices.RuntimeInformation", false)?.GetProperty("FrameworkDescription")
			.GetValue(null, null).ToString().StartsWith(".NET Core") ?? false;

		/// <summary>Throws a missing member runtime exception</summary>
		/// <param name="type">The type that is involved</param>
		/// <param name="names">A list of names</param>
		///
		public static void ThrowMissingMemberException(Type type, params string[] names)
		{
			var fields = string.Join(",", [.. GetFieldNames(type)]);
			var properties = string.Join(",", [.. GetPropertyNames(type)]);
			throw new MissingMemberException($"{string.Join(",", names)}; available fields: {fields}; available properties: {properties}");
		}

		/// <summary>Gets default value for a specific type</summary>
		/// <param name="type">The class/type</param>
		/// <returns>The default value</returns>
		///
		public static object GetDefaultValue(Type type)
		{
			if (type is null)
			{
				FileLog.Debug("AccessTools.GetDefaultValue: type is null");
				return null;
			}
			if (type == typeof(void))
				return null;
			if (type.IsValueType)
				return Activator.CreateInstance(type);
			return null;

View on GitHub (pinned to e7872dc170)