devlooped/moq · error · ArgumentException

Resources.TypeMatchersMayNotBeUsedWithCallbacks

Error message

Resources.TypeMatchersMayNotBeUsedWithCallbacks

What it means

SetCallbackBehavior throws this ArgumentException when a callback delegate's parameter types contain a Moq type matcher (It.IsAny<T>, It.Is<T>, It.Ref). Type matchers are only meaningful in setup argument expressions, not inside callbacks, which receive concrete invocation arguments.

Solutions

  1. Replace matcher types in the callback signature with the concrete parameter types
  2. Use It matchers only in the setup argument list, before the callback
  3. Filter within the callback body with plain if-conditions instead of matchers

Example fix

// before
mock.Setup(m => m.Save(It.IsAny<string>())).Callback((It.IsAny<string> s) => log(s));
// after
mock.Setup(m => m.Save(It.IsAny<string>())).Callback((string s) => log(s));
Defensive patterns

Strategy: type-guard

Validate before calling

foreach (var p in callback.GetMethodInfo().GetParameters())
  if (p.ParameterType.FullName?.StartsWith("Moq.It") == true) throw new InvalidOperationException("Type matchers not allowed in callback params");

Type guard

static bool HasNoTypeMatchers(Delegate d) => !d.GetMethodInfo().GetParameters().Any(p => p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition().Namespace == "Moq");

Try / catch

try { setup.Callback(cb); } catch (ArgumentException ex) when (ex.Message.Contains("Type matchers")) { /* use concrete types in callback signature */ }

Prevention

When it happens

Trigger: `.Callback((It.IsAny<int> x) => ...)` or passing a delegate whose signature uses Moq matcher types as callback parameters.

Common situations: Copy-pasting the setup's matcher expression into the Callback lambda signature; misunderstanding that callbacks receive actual runtime values.

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

Appendix: source

Thrown at src/Moq/MethodCall.cs:183

                {
                    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
        {
            Guard.NotNull(eventExpression, nameof(eventExpression));

            var expression = ExpressionReconstructor.Instance.ReconstructExpression(eventExpression, this.Mock.ConstructorArguments);

View on GitHub (pinned to 89a5be629c)