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
- Change the Callback delegate so its parameter list matches the mocked method's parameter count and types exactly
- If you don't need invocation arguments, use a parameterless Callback(() => ...)
- Update the setup after any change to the mocked interface/method signature
- 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
- Keep Callback lambdas' parameters mirroring the mocked method's signature
- Prefer parameterless Callback(() => ...) when arguments are not needed
- Update all setups whenever a mocked interface signature changes (IDE refactorings)
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
- Resources.InvalidReturnsCallbackNotADelegateWithReturnType
- Resources.InvalidCallbackReturnTypeMismatch (formatted with…
- callback (Argument is null)
- Resources.InvalidCallbackParameterMismatch (formatted with…
- Resources.InvalidCallbackNotADelegateWithReturnTypeVoid
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)