devlooped/moq · error · ArgumentException

Could not determine the correct positions for all argument…

Error message

Could not determine the correct positions for all argument matchers ({0} in total) used in a call to this method: {1}.
This could be caused by an unrecognized type conversion, coercion, narrowing, or widening, and is most likely a bug in Moq. Please report your use case to the Moq team.

What it means

During expression reconstruction Moq must map each recorded argument matcher back to a positional parameter of the invoked method. If leftover matchers remain after distribution (matchIndex < matches.Length), Moq cannot determine correct positions and throws ArgumentException, explicitly noting this is likely a Moq bug caused by an unrecognized type conversion/coercion/narrowing/widening.

Solutions

  1. Make matcher types exactly match the parameter types (use It.IsAny<short>() for a short parameter, not It.IsAny<int>()).
  2. Remove implicit conversions by casting explicitly so Moq can correlate matchers to positions.
  3. Disambiguate the target overload (cast the lambda or use explicit delegate types) so the recorded invocation matches the declared method.
  4. If types already match exactly, this is the suspected Moq bug the message mentions — reduce to a minimal repro and report it to the Moq team.

Example fix

// before
mock.SetupAction(x => x.Write(It.IsAny<int>())); // parameter is short
// after
mock.SetupAction(x => x.Write(It.IsAny<short>()));
Defensive patterns

Strategy: validation

Validate before calling

// Match It.IsAny<T> type arguments exactly to parameter types
Debug.Assert(matcherTypes.Zip(paramTypes, (m, p) => m == p).All(x => x), "Matcher/parameter type mismatch");

Type guard

static bool MatchersAlign(MethodInfo method, Type[] matcherTypes) =>
    method.GetParameters().Select(p => p.ParameterType).SequenceEqual(matcherTypes);

Try / catch

try
{
    mock.SetupAction(lambda);
}
catch (ArgumentException ex) when (ex.Message.Contains("positions for all argument matchers"))
{
    throw new InvalidOperationException("Matcher types must exactly match parameter types; otherwise report to Moq.", ex);
}

Prevention

When it happens

Trigger: Using argument matchers (It.IsAny<T>(), It.Is<T>(...)) inside a delegate-based setup where the number or arity of matchers does not line up with the invocation's parameters — typically via implicit conversions or overloads between matcher type and parameter type.

Common situations: Matchers with implicit numeric conversions (It.IsAny<int>() passed where a short/long/enum is expected); overloaded methods where the compiled delegate binds a different overload than the recorded invocation; boxing/casting of matcher arguments; generic type inference picking a mismatched matcher arity.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Moq/ActionObserver.cs:176

                                //  * the remaining matchers can't be distributed over the remaining parameters.
                                // In this case, we bail out, which will lead to an exception being thrown.
                                break;
                            }

                            // The remaining matchers can be distributed over the remaining parameters,
                            // so we can use up this matcher:
                            expressions[argumentIndex] = new MatchExpression(matches[matchIndex]);
                            ++matchIndex;
                        }
                    }

                    if (matchIndex < matches.Length)
                    {
                        // If we get here, we can be almost certain that matchers weren't distributed properly
                        // across the invocation's parameters. We could hope for the best and just leave it
                        // at that; however, it's probably better to let client code know, so it can be either
                        // adjusted or reported to Moq.
                        throw new ArgumentException(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                Resources.MatcherAssignmentFailedDuringExpressionReconstruction,
                                matches.Length,
                                $"{invocation.Method.DeclaringType!.GetFormattedName()}.{invocation.Method.Name}"));
                    }

                    bool CanDistribute(int msi, int asi)
                    {
                        var match = matches[msi];
                        var matchType = match.RenderExpression.Type;
                        for (int ai = asi; ai < expressions.Length; ++ai)
                        {
                            if (parameterTypes[ai].IsAssignableFrom(matchType)
                                && CanDistribute(msi + 1, ai + 1))
                            {
                                return true;
                            }

View on GitHub (pinned to 89a5be629c)