{"record":{"id":"a683e4412bfd04a0","repo":"pardeike/Harmony","slug":"return-type-of-pass-through-postfix-fix-does-not-match-type","errorCode":null,"errorMessage":"Return type of pass through postfix {fix} does not match type of its first parameter","messagePattern":"Return type of pass through postfix (.+?) does not match type of its first parameter","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Harmony/Internal/MethodCreator.cs","lineNumber":336,"sourceCode":"\t\t\t\t}\r\n\t\t\t\ttmpBoxVars.Do(tmpBoxVar =>\r\n\t\t\t\t{\r\n\t\t\t\t\tconfig.AddCode(new CodeInstruction(originalIsStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1));\r\n\t\t\t\t\tconfig.AddCode(Ldloc[tmpBoxVar.Key]);\r\n\t\t\t\t\tconfig.AddCode(Unbox_Any[tmpBoxVar.Value]);\r\n\t\t\t\t\tconfig.AddCode(Stobj[tmpBoxVar.Value]);\r\n\t\t\t\t});\r\n\r\n\t\t\t\tif (fix.ReturnType != typeof(void))\r\n\t\t\t\t{\r\n\t\t\t\t\tvar firstFixParam = fix.GetParameters().FirstOrDefault();\r\n\t\t\t\t\tvar hasPassThroughResultParam = firstFixParam is not null && fix.ReturnType == firstFixParam.ParameterType;\r\n\t\t\t\t\tif (hasPassThroughResultParam)\r\n\t\t\t\t\t\tresult = true;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (firstFixParam is not null)\r\n\t\t\t\t\t\t\tthrow new Exception($\"Return type of pass through postfix {fix} does not match type of its first parameter\");\r\n\r\n\t\t\t\t\t\tthrow new Exception($\"Postfix patch {fix} must have a \\\"void\\\" return type\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t}\r\n\r\n\t\tinternal bool AddFinalizers(bool catchExceptions)\r\n\t\t{\r\n\t\t\tvar rethrowPossible = true;\r\n\t\t\tvar original = config.original;\r\n\t\t\tvar originalIsStatic = original.IsStatic;\r\n\t\t\tconfig.finalizers.Do(fix =>\r\n\t\t\t{\r\n\t\t\t\tif (catchExceptions)\r\n\t\t\t\t\tconfig.AddCode(this.MarkBlock(ExceptionBlockType.BeginExceptionBlock));\r\n\r","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Internal/MethodCreator.cs#L318-L354","documentation":"Harmony detected a postfix declared as a 'pass through' postfix: one whose return type matches the type of its first parameter. However, the return type compared against the first parameter's type was not equal, so the pass-through contract is broken. Harmony requires that a pass-through postfix returns exactly the same type as its first argument (which receives the original method's return value), so it throws instead of silently emitting broken IL.","triggerScenarios":"Registering a postfix (via PatchClassProcessor/PatchMethods/CreateReplacement in the new MethodCreator pipeline) where the patch method's first parameter is typed to receive the result (pass-through style) but the method's declared return type differs from that parameter type, e.g. `static int Prefix(int result)` or `static object Postfix(string result)`.","commonSituations":"Refactoring the return type of a patch method without updating the first parameter; copying a pass-through patch and changing `int` to `long`; writing `object Postfix(T result)` hoping boxing works (it does not - exact type match is required).","solutions":["Make the patch method's return type exactly equal to the type of its first parameter (e.g. `static int Postfix(int result)`).","If you do not want pass-through semantics, change the return type to void so the check takes the normal postfix path.","If you only want to observe the result, use a void postfix with a parameter named `result` of the original's return type instead."],"exampleFix":"// before\nstatic string Postfix(int result) { return result.ToString(); }\n\n// after\nstatic int Postfix(int result) { Console.WriteLine(result); return result; }","handlingStrategy":"validation","validationCode":"var first = fix.GetParameters().FirstOrDefault();\nif (first is not null && fix.ReturnType != first.ParameterType)\n    throw new InvalidOperationException($\"Pass-through postfix {fix} must return exactly {first.ParameterType}\");","typeGuard":"bool IsValidPassThrough(MethodInfo fix) =>\n    fix.GetParameters().FirstOrDefault() is { } p && fix.ReturnType == p.ParameterType;","tryCatchPattern":"try { harmony.PatchAll(); }\ncatch (Exception ex) when (ex.Message.Contains(\"does not match type of its first parameter\")) { log.Error(ex); }","preventionTips":["Keep pass-through postfix signature as `static T Postfix(T result)` with identical T","Never box result into object in pass-through patches","Write a unit test that applies every patch in PatchAll before shipping"],"tags":["harmony","postfix","type-mismatch","patching"],"backgroundTag":"type-mismatch","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"}