pardeike/Harmony · error · ArgumentException

Unexpected null argument

Error message

Unexpected null argument

What it means

Transpilers.MethodReplacer validates its from argument before scanning instructions. If from (the method to be replaced in the IL) is null, it throws an ArgumentException with param name 'from' and message 'Unexpected null argument'.

Solutions

  1. Fix the lookup for 'from' so it resolves the target method
  2. Null-check both MethodBase values before calling MethodReplacer and throw/log a descriptive error
  3. Verify the replaced method exists in the current target assembly version

Example fix

// before
instructions.MethodReplacer(AccessTools.Method(typeof(UnityEngine.Time), "get_time"), AccessTools.Method(typeof(FakeTime), "get_time"));
// after
var from = AccessTools.Method(typeof(UnityEngine.Time), "get_time") ?? throw new Exception("Time.get_time not found");
var to = AccessTools.Method(typeof(FakeTime), "get_time");
instructions.MethodReplacer(from, to);
Defensive patterns

Strategy: validation

Validate before calling

var from = AccessTools.Method(fromType, fromName) ?? throw new InvalidOperationException($"Replaced method {fromType}.{fromName} not found");

Try / catch

try { instructions = instructions.MethodReplacer(from, to); }
catch (ArgumentException ex) when (ex.ParamName == "from") { logger.Error("Transpiler 'from' method lookup failed"); throw; }

Prevention

When it happens

Trigger: Passing a null MethodBase as the 'from' parameter — typically the result of a failed AccessTools.Method/Constructor lookup inside a transpiler that calls instructions.MethodReplacer(from, to).

Common situations: Transpiler authors inline AccessTools lookups directly into the MethodReplacer call; a typo or renamed target method in a game update makes the lookup return null at patch time.

Related errors


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

Appendix: source

Thrown at Harmony/Public/Transpilers.cs:22

using System.Reflection;
using System.Reflection.Emit;

namespace HarmonyLib
{
	/// <summary>A collection of commonly used transpilers</summary>
	/// 
	public static class Transpilers
	{
		/// <summary>A transpiler that replaces all occurrences of a given method with another one using the same signature</summary>
		/// <param name="instructions">The enumeration of <see cref="CodeInstruction"/> to act on</param>
		/// <param name="from">Method or constructor to search for</param>
		/// <param name="to">Method or constructor to replace with</param>
		/// <returns>Modified enumeration of <see cref="CodeInstruction"/></returns>
		///
		public static IEnumerable<CodeInstruction> MethodReplacer(this IEnumerable<CodeInstruction> instructions, MethodBase from, MethodBase to)
		{
			if (from is null)
				throw new ArgumentException("Unexpected null argument", nameof(from));
			if (to is null)
				throw new ArgumentException("Unexpected null argument", nameof(to));

			foreach (var instruction in instructions)
			{
				var method = instruction.operand as MethodBase;
				if (method == from)
				{
					instruction.opcode = to.IsConstructor ? OpCodes.Newobj : OpCodes.Call;
					instruction.operand = to;
				}
				yield return instruction;
			}
		}

		/// <summary>A transpiler that alters instructions that match a predicate by calling an action</summary>
		/// <param name="instructions">The enumeration of <see cref="CodeInstruction"/> to act on</param>
		/// <param name="predicate">A predicate selecting the instructions to change</param>

View on GitHub (pinned to e7872dc170)