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
- Replace the void-returning delegate with a Func whose return type matches (or is assignable to) the mocked method's return type
- If you only want a side effect, use .Callback(...) for the side effect and .Returns(value) for the result
- 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
- Use Callback(...) for side effects and Returns(...) only with value-returning delegates
- Check that lambdas passed to Returns have a non-void result
- Avoid passing Action delegates into Returns<T>
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
- Resources.InvalidCallbackReturnTypeMismatch (formatted with…
- Resources.InvalidCallbackNotADelegateWithReturnTypeVoid
- Resources.InvalidCallbackParameterCountMismatch (formatted…
- callback (Argument is null)
- Resources.InvalidCallbackParameterMismatch (formatted with…
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)