devlooped/moq · error · ArgumentException

Resources.InvalidCallbackNotADelegateWithReturnTypeVoid

Error message

Resources.InvalidCallbackNotADelegateWithReturnTypeVoid

What it means

SetCallbackBehavior throws this ArgumentException when a callback delegate used on a setup that returns a value has a non-void return type. Callbacks must be Action-like (return void); Func delegates with return values are rejected — use Returns for values.

Solutions

  1. Use a void-returning lambda/Action for Callback()
  2. Move the value computation to Returns() if a return value is needed
  3. If the callback should produce the return value, use Returns with a matching Func

Example fix

// before
mock.Setup(m => m.GetValue()).Callback(() => ComputeValue());
// after
mock.Setup(m => m.GetValue()).Returns(() => ComputeValue());
Defensive patterns

Strategy: validation

Validate before calling

if (callback.GetMethodInfo().ReturnType != typeof(void)) throw new InvalidOperationException("Callback must return void; use Returns for values");

Type guard

static bool IsVoidCallback(Delegate d) => d.GetMethodInfo().ReturnType == typeof(void);

Try / catch

try { setup.Callback(cb); } catch (ArgumentException ex) when (ex.Message.Contains("void")) { /* use Returns instead */ }

Prevention

When it happens

Trigger: Passing a Func (e.g. `Callback(() => someValue)`) or a method group returning a value to .Callback() on a setup.

Common situations: Developers confusing Callback with Returns; reusing an existing Func delegate as a callback; expression-bodied members that return values.

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/f95563b2452d82de. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/MethodCall.cs:178

            }
            else
            {
                var expectedParamTypes = this.Method.GetParameterTypes();
                if (!callback.CompareParameterTypesTo(expectedParamTypes))
                {
                    throw new ArgumentException(
                        string.Format(
                            CultureInfo.CurrentCulture,
                            Resources.InvalidCallbackParameterMismatch,
                            this.Method.GetParameterTypeList(),
                            callback.GetMethodInfo().GetParameterTypeList()));
                }

                var callbackMethod = callback.GetMethodInfo();

                if (callbackMethod.ReturnType != typeof(void))
                {
                    throw new ArgumentException(Resources.InvalidCallbackNotADelegateWithReturnTypeVoid, nameof(callback));
                }

                if (callbackMethod.GetParameterTypes().Any(Extensions.IsOrContainsTypeMatcher))
                {
                    throw new ArgumentException(Resources.TypeMatchersMayNotBeUsedWithCallbacks);
                }

                behavior = new Callback(invocation => callback.InvokePreserveStack(invocation.Arguments));
            }
        }

        public void SetFailMessage(string failMessage)
        {
            this.failMessage = failMessage;
        }

        public void SetRaiseEventBehavior<TMock>(Action<TMock> eventExpression, Delegate func)
            where TMock : class

View on GitHub (pinned to 89a5be629c)