devlooped/moq · error · ArgumentException

Resources.InvalidCallbackParameterCountMismatch (formatted…

Error message

Resources.InvalidCallbackParameterCountMismatch (formatted with expected and actual parameter counts)

What it means

Moq validates that a Callback(...) delegate passed alongside Returns/Throws (via Setup with computed value behaviors) receives exactly the same number of parameters as the mocked method. This ArgumentException is thrown when the callback delegate's parameter count differs from the method's parameter count, so Moq could not bind invocation arguments to the callback.

Solutions

  1. Change the Callback delegate so its parameter list matches the mocked method's parameter count and types exactly
  2. If you don't need invocation arguments, use a parameterless Callback(() => ...)
  3. Update the setup after any change to the mocked interface/method signature
  4. Use It.IsAny<T>() placeholders in the setup expression to keep the setup resilient

Example fix

// before
mock.Setup(x => x.Save(It.IsAny<string>(), It.IsAny<int>()))
    .Callback((string name) => Console.WriteLine(name))
    .Returns(true);
// after
mock.Setup(x => x.Save(It.IsAny<string>(), It.IsAny<int>()))
    .Callback((string name, int count) => Console.WriteLine(name))
    .Returns(true);
Defensive patterns

Strategy: validation

Validate before calling

static void ValidateCallbackArity(MethodInfo mockedMethod, Delegate callback)
{
    int cbParams = callback.Method.GetParameters().Length;
    int expected = mockedMethod.GetParameters().Length;
    if (cbParams != 0 && cbParams != expected)
        throw new InvalidOperationException(
            $"Callback has {cbParams} params but mocked method has {expected}");
}

Type guard

bool CallbackArityMatches<TMock>(Expression<Func<TMock, object>> setup, Delegate cb)
{
    var method = ((MethodCallExpression)setup.Body).Method;
    int n = cb.Method.GetParameters().Length;
    return n == 0 || n == method.GetParameters().Length;
}

Try / catch

try
{
    mock.Setup(x => x.Save(It.IsAny<string>(), It.IsAny<int>()))
        .Callback((string s, int i) => log(s, i))
        .Returns(true);
}
catch (ArgumentException ex) when (ex.Message.Contains("parameter"))
{
    // fix the callback parameter count in the test
    throw;
}

Prevention

When it happens

Trigger: Calling Setup(...).Callback(lambdaWithDifferentArity).Returns(...)/.Throws(...) where the callback lambda declares more or fewer parameters than the mocked method (e.g. a 2-arg callback on a 1-arg method); using a static/extension method group whose effective parameter count mismatches.

Common situations: Refactoring a mocked method signature (adding/removing parameters) without updating the Callback lambda; copying a Callback from another setup; declaring a callback with optional or extra capture parameters.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/b1a559402f677214. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/MethodCall.cs:397

        }

        void ValidateNumberOfCallbackParameters(Delegate callback, MethodInfo callbackMethod)
        {
            var numberOfActualParameters = callbackMethod.GetParameters().Length;
            if (callbackMethod.IsStatic)
            {
                if (callbackMethod.IsExtensionMethod() || callback.Target != null)
                {
                    numberOfActualParameters--;
                }
            }

            if (numberOfActualParameters > 0)
            {
                var numberOfExpectedParameters = this.Method.GetParameters().Length;
                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);
            }

View on GitHub (pinned to 89a5be629c)