devlooped/moq · error · ArgumentException
Resources.InvalidCallbackParameterMismatch (formatted with…
Error message
Resources.InvalidCallbackParameterMismatch (formatted with expected and actual parameter type lists)
What it means
SetCallbackBehavior throws this ArgumentException when the callback delegate's parameter types do not match the mocked method's parameter types. Moq validates via CompareParameterTypesTo so the callback receives the exact arguments of the invocation.
Solutions
- Update the Callback lambda so its parameter list exactly matches the mocked method's parameters in order and type
- If no parameters are needed, use a parameterless `Callback(() => ...)`
- Check overload resolution — ensure the setup targets the intended overload
Example fix
// before
mock.Setup(m => m.Save("key", 5)).Callback((int id) => log(id));
// after
mock.Setup(m => m.Save("key", 5)).Callback((string key, int id) => log(key, id)); Defensive patterns
Strategy: validation
Validate before calling
var cbParams = callback.GetMethodInfo().GetParameters();
var methodParams = method.GetParameters();
if (cbParams.Length != methodParams.Length) throw new InvalidOperationException("Callback parameter count must match mocked method"); Type guard
static bool CallbackParamsMatch(Delegate d, MethodInfo mocked) => d.GetMethodInfo().GetParameters().Select(p => p.ParameterType).SequenceEqual(mocked.GetParameters().Select(p => p.ParameterType));
Try / catch
try { setup.Callback(cb); } catch (ArgumentException ex) when (ex.Message.Contains("callback")) { /* align callback signature */ } Prevention
- Mirror the mocked method's parameter list exactly in Callback lambdas
- Use parameterless Callback(() => ...) when arguments aren't needed
- Update Callbacks alongside method signature changes
When it happens
Trigger: `.Callback(lambda)` whose lambda parameters differ in count, order, or type from the mocked method, e.g. mocking `Save(string, int)` with `.Callback((int id) => ...)`.
Common situations: Refactorings that changed the mocked method signature without updating Callback lambdas; using a shared helper lambda across setups with different signatures.
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.ArgumentMatcherWillNeverMatch (formatted with…
- callback (Argument is null)
- Resources.InvalidCallbackNotADelegateWithReturnTypeVoid
- Resources.TypeMatchersMayNotBeUsedWithCallbacks
- Resources.InvalidCallbackParameterCountMismatch (formatted…
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/ab393aa6d9f1268e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/MethodCall.cs:166
: ref this.afterReturnCallback;
if (callback is Action callbackWithoutArguments)
{
behavior = new Callback(_ => callbackWithoutArguments());
}
else if (callback.GetType() == typeof(Action<IInvocation>))
{
// NOTE: Do NOT rewrite the above condition as `callback is Action<IInvocation>`,
// because this will also yield true if `callback` is a `Action<object>` and thus
// break existing uses of `(object arg) => ...` callbacks!
behavior = new Callback((Action<IInvocation>)callback);
}
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);
}View on GitHub (pinned to 89a5be629c)