pardeike/Harmony · error · Exception

No parameter found at index

Error message

No parameter found at index {argumentIdx}

What it means

Harmony lets patch methods inject the n-th argument of the original method by naming a parameter like "__0" (the PARAM_INDEX_PREFIX). EmitCallParameter throws this when the parsed index is negative or >= the original method's parameter count, i.e. the patch asks for an argument slot the original method does not have. It protects the emitted IL from indexing past the end of the parameter array.

Solutions

  1. Count the original method's parameters and change the injected index parameter to a valid one (0..count-1).
  2. Verify the target MethodBase resolved by Harmony is the overload you expect (use AccessTools.Method with explicit types).
  3. If the index was copied from another patch, replace it with the matching named parameter instead of an index.

Example fix

// before
static void Postfix(ref int __3) { }
// original has 2 params

// after
static void Postfix(ref int __1) { }
Defensive patterns

Strategy: validation

Validate before calling

var paramCount = original.GetParameters().Length;
foreach (var p in patchMethod.GetParameters())
{
    var name = p.Name;
    if (name.StartsWith("__") && int.TryParse(name.Substring(2), out var idx))
        if (idx < 0 || idx >= paramCount)
            throw new InvalidOperationException($"{name} out of range for {original} ({paramCount} params)");
}

Type guard

static bool IsValidArgIndex(string paramName, int paramCount) =>
    paramName.StartsWith("__") && int.TryParse(paramName.Substring(2), out var i) && i >= 0 && i < paramCount;

Try / catch

try { harmony.Patch(original, prefix: prefix); }
catch (Exception ex) when (ex.Message.Contains("No parameter found at index"))
{ Logger.Error($"Invalid __N injection on {original}: {ex.Message}"); }

Prevention

When it happens

Trigger: Declaring a patch parameter named "__N" (or "___idx"-style index prefix) where N >= originalParameters.Length or N < 0, e.g. injecting "__3" into a 2-argument method, then patching and running EmitCallParameter during wrapper generation.

Common situations: The original method signature changed between library/game versions (an overload with fewer parameters is now bound), or the patch was copied from code targeting a different overload with more parameters.

Related errors


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

Appendix: source

Thrown at Harmony/Internal/MethodCreatorTools.cs:386

					var ldlocCode = paramType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
					codes.Add(new CodeInstruction(ldlocCode, localBuilder));
					continue;
				}

				int argumentIdx;
				if (injection.argumentMode == ArgumentMode.Original)
				{
					argumentIdx = Array.IndexOf(originalParameterNames, paramRealName);
					if (argumentIdx == -1)
						throw new Exception($"Parameter \"{paramRealName}\" not found in method {original.FullDescription()}");
				}
				else if (paramRealName.StartsWith(PARAM_INDEX_PREFIX, StringComparison.Ordinal))
				{
					var val = paramRealName.Substring(PARAM_INDEX_PREFIX.Length);
					if (!int.TryParse(val, out argumentIdx))
						throw new Exception($"Parameter {paramRealName} does not contain a valid index");
					if (argumentIdx < 0 || argumentIdx >= originalParameters.Length)
						throw new Exception($"No parameter found at index {argumentIdx}");
				}
				else
				{
					argumentIdx = patch.GetArgumentIndex(originalParameterNames, injection.parameterInfo);
					if (argumentIdx == -1)
					{
						var harmonyMethod = HarmonyMethodExtensions.GetMergedFromType(paramType);
						harmonyMethod.methodType ??= MethodType.Normal;
						var delegateOriginal = harmonyMethod.GetOriginalMethod();
						if (delegateOriginal is MethodInfo methodInfo)
						{
							var delegateConstructor = paramType.GetConstructor([typeof(object), typeof(IntPtr)]);
							if (delegateConstructor is not null)
							{
								if (methodInfo.IsStatic)
									codes.Add(Ldnull);
								else
								{

View on GitHub (pinned to e7872dc170)