pardeike/Harmony · error · ArgumentException

No field found at given index in class

Error message

No field found at given index in class {originalType?.AssemblyQualifiedName ?? "null"}

What it means

When binding an injected patch parameter whose name uses the instance-field-by-index convention (`__N` derived from INSTANCE_FIELD_PREFIX), Harmony resolves the Nth declared field of the patched type via AccessTools.DeclaredField. If no declared field exists at that index (type has fewer fields, or ordering changed), this ArgumentException is thrown with the field's argument name.

Solutions

  1. Reduce the index or verify the target type's declared field count/order at runtime (e.g. via AccessTools.GetDeclaredFields(originalType)).
  2. Prefer referencing the field by name instead of index to be resilient to reordering.
  3. Pin the target assembly version or add a startup check that the expected field exists before applying the patch.

Example fix

// before
static void Postfix(MyClass __this, int __5) { ... }

// after
static void Postfix(MyClass __this, int ___myFieldName) { ... }
Defensive patterns

Strategy: validation

Validate before calling

var fields = AccessTools.GetDeclaredFields(targetType);
if (index < 0 || index >= fields.Count)
    throw new InvalidOperationException($"{targetType} has no declared field at index {index}");

Type guard

bool HasFieldAtIndex(Type t, int i) => AccessTools.DeclaredField(t, i) is not null;

Try / catch

try { harmony.PatchAll(); }
catch (ArgumentException ex) when (ex.Message.Contains("No field found at given index")) { log.Error(ex); }

Prevention

When it happens

Trigger: Naming a patch parameter with the instance-field numeric prefix (e.g. `__0`, `__2`) in a patch for a type that has no declared field at that index; the target type changed between versions so field order/count no longer matches.

Common situations: Target assembly updated and fields reordered/removed; patch written against a different class than intended; using the index form on a struct/class where all fields are inherited rather than declared.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at Harmony/Internal/MethodCreatorTools.cs:291

				if (injectionType == InjectionType.ArgsArray)
				{
					if (config.localVariables.TryGetValue(InjectionType.ArgsArray, out var argsArrayVar))
						codes.Add(Ldloc[argsArrayVar]);
					else
						codes.Add(Ldnull);
					continue;
				}

				if (injection.argumentMode != ArgumentMode.Original && paramRealName.StartsWith(INSTANCE_FIELD_PREFIX, StringComparison.Ordinal))
				{
					var fieldName = paramRealName.Substring(INSTANCE_FIELD_PREFIX.Length);
					FieldInfo fieldInfo;
					if (fieldName.All(char.IsDigit))
					{
						fieldInfo = AccessTools.DeclaredField(originalType, int.Parse(fieldName));
						if (fieldInfo is null)
							throw new ArgumentException($"No field found at given index in class {originalType?.AssemblyQualifiedName ?? "null"}", fieldName);
					}
					else
					{
						fieldInfo = AccessTools.Field(originalType, fieldName);
						if (fieldInfo is null)
							throw new ArgumentException($"No such field defined in class {originalType?.AssemblyQualifiedName ?? "null"}", fieldName);
					}

					if (fieldInfo.IsStatic)
						codes.Add(paramType.IsByRef ? Ldsflda[fieldInfo] : Ldsfld[fieldInfo]);
					else
					{
						codes.Add(Ldarg_0);
						codes.Add(paramType.IsByRef ? Ldflda[fieldInfo] : Ldfld[fieldInfo]);
					}
					continue;
				}

View on GitHub (pinned to e7872dc170)