pardeike/Harmony · error · Exception

Cannot use with non-ref return type of method

Error message

Cannot use {InjectionType.ResultRef} with non-ref return type {returnType.FullName} of method {original.FullDescription()}

What it means

A patch parameter requests a reference to the original method's return value (InjectionType.ResultRef, backed by RefResult<T>). Harmony only supports this when the original method returns by ref (`ref T`); otherwise there is no ref location to hand out, so it throws listing the actual return type.

Solutions

  1. Use a normal result injection (InjectionType.Result) instead of ResultRef for by-value returns.
  2. If you need to mutate the result, use a `ref T` parameter pass-through postfix rather than ResultRef.
  3. Confirm the target signature actually returns `ref T` (check MethodInfo.ReturnType.IsByRef) before registering the patch.

Example fix

// before (target returns int, not ref int)
static void Postfix([HarmonyArgument("resultref")] RefResult<int> resultRef) { ... }

// after
static void Postfix(ref int result) { ... }
Defensive patterns

Strategy: validation

Validate before calling

if (usesResultRef && !original.ReturnType.IsByRef)
    throw new InvalidOperationException("ResultRef requires a ref-returning method");

Type guard

bool SupportsResultRef(MethodInfo original) => original.ReturnType.IsByRef;

Try / catch

try { harmony.Patch(original, postfix: new HarmonyMethod(fix)); }
catch (Exception ex) when (ex.Message.Contains("with non-ref return type")) { log.Error(ex); }

Prevention

When it happens

Trigger: Declaring a ResultRef-injected parameter (RefResult<T>) in a patch targeting a method whose return type is not IsByRef - i.e. any normal by-value return, including `void` and plain `T`.

Common situations: Applying a ref-returning patch to a non-ref overload of the same method name; library update removed `ref` from the target's signature while the patch still assumes it.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at Harmony/Internal/MethodCreatorTools.cs:351

					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;
				}

				if (injectionType == InjectionType.ResultRef)
				{
					if (!returnType.IsByRef)
						throw new Exception(
							 $"Cannot use {InjectionType.ResultRef} with non-ref return type {returnType.FullName} of method {original.FullDescription()}");

					var resultType = paramType;
					var expectedTypeRef = typeof(RefResult<>).MakeGenericType(returnType.GetElementType()).MakeByRefType();
					if (resultType != expectedTypeRef)
						throw new Exception(
							 $"Wrong type of {InjectedParameter.RESULT_REF_VAR} for method {original.FullDescription()}. Expected {expectedTypeRef.FullName}, got {resultType.FullName}");

					codes.Add(Ldloca[config.GetLocal(InjectionType.ResultRef)]);

					refResultUsed = true;
					continue;
				}

				if (injection.argumentMode != ArgumentMode.Original && config.localVariables.TryGetValue(paramRealName, out var localBuilder))
				{
					var ldlocCode = paramType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
					codes.Add(new CodeInstruction(ldlocCode, localBuilder));

View on GitHub (pinned to e7872dc170)