{"record":{"id":"92977472ad1072ca","repo":"pardeike/Harmony","slug":"postfix-patch-fix-must-have-a-void-return-type","errorCode":null,"errorMessage":"Postfix patch {fix} must have a \"void\" return type","messagePattern":"Postfix patch (.+?) must have a \"void\" return type","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Harmony/Internal/MethodCreator.cs","lineNumber":338,"sourceCode":"\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\n\t\t\t\tvar tmpBoxVars = new List<KeyValuePair<LocalBuilder, Type>>();\r\n\t\t\t\tconfig.AddCodes(this.EmitCallParameter(fix, false, out var tmpInstanceBoxingVar, out var tmpObjectVar, out var refResultUsed, tmpBoxVars));\r","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/pardeike/Harmony/blob/e7872dc17008bc0ca2b3de1f53fd4120dbd7a17c/Harmony/Internal/MethodCreator.cs#L320-L356","documentation":"Harmony requires a non-pass-through postfix to return void (or to be a valid pass-through). When the postfix's return type differs from its first parameter's type (so it is not pass-through) and the return type is not void/continuable, Harmony cannot integrate the return value and throws. This guards against silently ignoring a postfix return value.","triggerScenarios":"Registering a postfix whose return type is a non-void type that does not match its first parameter's type, e.g. `static bool Postfix()` or `static int Postfix(object result)`, through PatchClassProcessor/CreateReplacement.","commonSituations":"Writing a postfix that accidentally returns a value (forgot `void`); moving a prefix's body into a postfix while keeping its bool/int return; generic patch methods whose return type doesn't unify with the result parameter.","solutions":["Declare the postfix as `static void Postfix(...)`.","If you intend to modify the result, make it a pass-through: first parameter of type T (receiving the result) and return type exactly T.","If you intended conditional skipping of the original method, that belongs in a prefix returning bool, not a postfix."],"exampleFix":"// before\nstatic int Postfix(int result) { Log(result); return result + 1; }\n\n// after\nstatic void Postfix(ref int result) { Log(result); result = result + 1; }","handlingStrategy":"validation","validationCode":"var first = fix.GetParameters().FirstOrDefault();\nif (fix.ReturnType != typeof(void) && (first is null || fix.ReturnType != first.ParameterType))\n    throw new InvalidOperationException($\"{fix} must be void or a valid pass-through\");","typeGuard":"bool IsValidPostfix(MethodInfo fix) =>\n    fix.ReturnType == typeof(void) ||\n    (fix.GetParameters().FirstOrDefault() is { } p && fix.ReturnType == p.ParameterType);","tryCatchPattern":"try { processor.Patch(); }\ncatch (Exception ex) when (ex.Message.Contains(\"must have a \\\"void\\\" return type\")) { log.Error(ex); }","preventionTips":["Default to void postfixes; use `ref T result` to mutate results","Don't copy prefix signatures into postfixes","Review patch method return types in code review"],"tags":["harmony","postfix","return-type","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"}