{"record":{"id":"94e6d25c4eb7922c","repo":"pardeike/Harmony","slug":"unexpected-null-argument","errorCode":null,"errorMessage":"Unexpected null argument","messagePattern":"Unexpected null argument","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"Harmony/Public/Transpilers.cs","lineNumber":22,"sourceCode":"using System.Reflection;\r\nusing System.Reflection.Emit;\r\n\r\nnamespace HarmonyLib\r\n{\r\n\t/// <summary>A collection of commonly used transpilers</summary>\r\n\t/// \r\n\tpublic static class Transpilers\r\n\t{\r\n\t\t/// <summary>A transpiler that replaces all occurrences of a given method with another one using the same signature</summary>\r\n\t\t/// <param name=\"instructions\">The enumeration of <see cref=\"CodeInstruction\"/> to act on</param>\r\n\t\t/// <param name=\"from\">Method or constructor to search for</param>\r\n\t\t/// <param name=\"to\">Method or constructor to replace with</param>\r\n\t\t/// <returns>Modified enumeration of <see cref=\"CodeInstruction\"/></returns>\r\n\t\t///\r\n\t\tpublic static IEnumerable<CodeInstruction> MethodReplacer(this IEnumerable<CodeInstruction> instructions, MethodBase from, MethodBase to)\r\n\t\t{\r\n\t\t\tif (from is null)\r\n\t\t\t\tthrow new ArgumentException(\"Unexpected null argument\", nameof(from));\r\n\t\t\tif (to is null)\r\n\t\t\t\tthrow new ArgumentException(\"Unexpected null argument\", nameof(to));\r\n\r\n\t\t\tforeach (var instruction in instructions)\r\n\t\t\t{\r\n\t\t\t\tvar method = instruction.operand as MethodBase;\r\n\t\t\t\tif (method == from)\r\n\t\t\t\t{\r\n\t\t\t\t\tinstruction.opcode = to.IsConstructor ? OpCodes.Newobj : OpCodes.Call;\r\n\t\t\t\t\tinstruction.operand = to;\r\n\t\t\t\t}\r\n\t\t\t\tyield return instruction;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t/// <summary>A transpiler that alters instructions that match a predicate by calling an action</summary>\r\n\t\t/// <param name=\"instructions\">The enumeration of <see cref=\"CodeInstruction\"/> to act on</param>\r\n\t\t/// <param name=\"predicate\">A predicate selecting the instructions to change</param>\r","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Public/Transpilers.cs#L4-L40","documentation":"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'.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Fix the lookup for 'from' so it resolves the target method","Null-check both MethodBase values before calling MethodReplacer and throw/log a descriptive error","Verify the replaced method exists in the current target assembly version"],"exampleFix":"// before\ninstructions.MethodReplacer(AccessTools.Method(typeof(UnityEngine.Time), \"get_time\"), AccessTools.Method(typeof(FakeTime), \"get_time\"));\n// after\nvar from = AccessTools.Method(typeof(UnityEngine.Time), \"get_time\") ?? throw new Exception(\"Time.get_time not found\");\nvar to = AccessTools.Method(typeof(FakeTime), \"get_time\");\ninstructions.MethodReplacer(from, to);","handlingStrategy":"validation","validationCode":"var from = AccessTools.Method(fromType, fromName) ?? throw new InvalidOperationException($\"Replaced method {fromType}.{fromName} not found\");","typeGuard":null,"tryCatchPattern":"try { instructions = instructions.MethodReplacer(from, to); }\ncatch (ArgumentException ex) when (ex.ParamName == \"from\") { logger.Error(\"Transpiler 'from' method lookup failed\"); throw; }","preventionTips":["Resolve and null-check both MethodBase values before building the transpiler pipeline","Use static cached MethodInfo fields initialized once with null checks","Add a startup self-test that runs the transpiler on an empty instruction list to catch null lookups early"],"tags":["csharp","transpiler","null-argument","il","harmony"],"backgroundTag":"null-argument","analyzedSha":"e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c","analyzedAt":"2026-09-15T22:47:11.550Z","contentChangedAt":"2026-09-15T22:47:11.550Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}