devlooped/moq · error · ArgumentException

Resources.InvalidReturnsCallbackNotADelegateWithReturnType

Error message

Resources.InvalidReturnsCallbackNotADelegateWithReturnType

What it means

When a Callback is combined with Returns (or Throws) computed-value behavior, Moq requires the callback delegate to return a value assignable to the mocked method's return type. This ArgumentException is thrown when the callback delegate's return type is void, i.e. it is an Action-style delegate used where a Func-style delegate with a return type is required.

Solutions

  1. Replace the void-returning delegate with a Func whose return type matches (or is assignable to) the mocked method's return type
  2. If you only want a side effect, use .Callback(...) for the side effect and .Returns(value) for the result
  3. Ensure the lambda's last expression returns a value of the expected type

Example fix

// before
mock.Setup(x => x.Compute(It.IsAny<int>()))
    .Returns((int i) => Console.WriteLine(i)); // void lambda
// after
mock.Setup(x => x.Compute(It.IsAny<int>()))
    .Returns((int i) => i * 2);
Defensive patterns

Strategy: validation

Validate before calling

static void ValidateReturnsCallback(MethodInfo mockedMethod, Delegate cb)
{
    if (cb.Method.ReturnType == typeof(void))
        throw new InvalidOperationException(
            "Returns callback must return a value; use Callback() for side effects");
}

Type guard

static bool IsValidReturnsCallback<TMethod>(Expression<Func<TMethod, object>> _, Delegate cb)
    => cb.Method.ReturnType != typeof(void);

Try / catch

try
{
    mock.Setup(x => x.Compute(It.IsAny<int>()))
        .Returns((int i) => i * 2);
}
catch (ArgumentException ex) when (ex.Message.Contains("return type"))
{
    // replace void delegate with a Func returning the expected type
    throw;
}

Prevention

When it happens

Trigger: Calling Setup(x => x.Method()).Callback<T>(a void-returning delegate).Returns(...) — e.g. passing an Action<T> or a lambda like x => Console.WriteLine(x) in a position where the computed return value must come from the callback's return value.

Common situations: Mixing up Callback(...) with Returns(callback) — the void delegate belongs in Callback, while Returns needs a Func; converting a setup from Callback+field-capture style to Returns-computed style; refactoring where a void helper method was passed directly.

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 devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/865be90c69f6d626. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/MethodCall.cs:413

                if (numberOfActualParameters != numberOfExpectedParameters)
                {
                    throw new ArgumentException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            Resources.InvalidCallbackParameterCountMismatch,
                            numberOfExpectedParameters,
                            numberOfActualParameters));
                }
            }
        }

        void ValidateCallbackReturnType(MethodInfo callbackMethod, Type expectedReturnType)
        {
            var actualReturnType = callbackMethod.ReturnType;

            if (actualReturnType == typeof(void))
            {
                throw new ArgumentException(Resources.InvalidReturnsCallbackNotADelegateWithReturnType);
            }

            if (!expectedReturnType.IsAssignableFrom(actualReturnType))
            {
                // TODO: If the return type is a matcher, does the callback's return type need to be matched against it?
                if (typeof(ITypeMatcher).IsAssignableFrom(expectedReturnType) == false)
                {
                    throw new ArgumentException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            Resources.InvalidCallbackReturnTypeMismatch,
                            expectedReturnType.GetFormattedName(),
                            actualReturnType.GetFormattedName()));
                }
            }
        }
    }
}

View on GitHub (pinned to 89a5be629c)