pardeike/Harmony · error · Exception

Cannot assign method return type

Error message

Cannot assign method return type {returnType.FullName} to {InjectedParameter.RESULT_VAR} type {resultType.FullName} for method {original.FullDescription()}

What it means

The patch requests the original's return value (InjectionType.Result), but the declared type of the injected parameter is not assignable from the original method's return type. Harmony validates `resultType.IsAssignableFrom(returnType)` before emitting the load of the result local and throws when types are incompatible, preventing invalid IL.

Solutions

  1. Change the injected result parameter's type to exactly the original method's return type (or a compatible base/interface).
  2. Use `object` and box manually if you need a generic patch, respecting value-type handling.
  3. Verify original.FullDescription() return type at patch-application time and pick/branch to the right patch method.

Example fix

// before (target returns int)
static void Postfix([HarmonyArgument("result")] string result) { ... }

// after
static void Postfix([HarmonyArgument("result")] int result) { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

var paramType = resultParam.ParameterType;
if (!paramType.IsAssignableFrom(original.ReturnType))
    throw new InvalidOperationException($"Result param {paramType} cannot accept {original.ReturnType}");

Type guard

bool ResultTypeMatches(MethodInfo original, ParameterInfo p) =>
    p.ParameterType.IsAssignableFrom(original.ReturnType);

Try / catch

try { harmony.Patch(original, postfix: new HarmonyMethod(fix)); }
catch (Exception ex) when (ex.Message.Contains("Cannot assign method return type")) { log.Error(ex); }

Prevention

When it happens

Trigger: Declaring a result-injected parameter of a type that the original's return type cannot be assigned to, e.g. `string` result parameter on a method returning `int`, or an unboxed value-type parameter for a boxed/object return.

Common situations: Target method return type changed between versions (int -> long, T -> object); patch shared across overloads with different return types; using `ref`/`out` parameter forms that don't match the result type rules.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at Harmony/Internal/MethodCreatorTools.cs:328

				if (injectionType == InjectionType.State)
				{
					var ldlocCode = paramType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
					if (config.localVariables.TryGetValue(patch.DeclaringType?.AssemblyQualifiedName ?? "null", out var stateVar))
						codes.Add(new CodeInstruction(ldlocCode, stateVar));
					else
						codes.Add(Ldnull);
					continue;
				}

				if (injectionType == InjectionType.Result)
				{
					if (returnType == typeof(void))
						throw new Exception($"Cannot get result from void method {original.FullDescription()}");
					var resultType = paramType;
					if (resultType.IsByRef && returnType.IsByRef is false)
						resultType = resultType.GetElementType();
					if (resultType.IsAssignableFrom(returnType) is false)
						throw new Exception($"Cannot assign method return type {returnType.FullName} to {InjectedParameter.RESULT_VAR} type {resultType.FullName} for method {original.FullDescription()}");
					var ldlocCode = paramType.IsByRef && returnType.IsByRef is false ? OpCodes.Ldloca : OpCodes.Ldloc;
					if (returnType.IsValueType && paramType == typeof(object).MakeByRefType())
						ldlocCode = OpCodes.Ldloc;
					codes.Add(new CodeInstruction(ldlocCode, config.GetLocal(InjectionType.Result)));
					if (returnType.IsValueType)
					{
						if (paramType == typeof(object))
							codes.Add(Box[returnType]);
						else if (paramType == typeof(object).MakeByRefType())
						{
							codes.Add(Box[returnType]);
							tmpObjectVar = config.DeclareLocal(typeof(object));
							codes.Add(Stloc[tmpObjectVar]);
							codes.Add(Ldloca[tmpObjectVar]);
						}
					}
					continue;
				}

View on GitHub (pinned to e7872dc170)