{"record":{"id":"80927c2ce27e6510","repo":"devlooped/moq","slug":"could-not-determine-the-correct-positions-for-all-argument","errorCode":null,"errorMessage":"Could not determine the correct positions for all argument matchers ({0} in total) used in a call to this method: {1}.\nThis 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.","messagePattern":"Could not determine the correct positions for all argument matchers \\((.+?) in total\\) used in a call to this method: (.+?)\\.\nThis 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\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/ActionObserver.cs","lineNumber":176,"sourceCode":"                                //  * the remaining matchers can't be distributed over the remaining parameters.\n                                // In this case, we bail out, which will lead to an exception being thrown.\n                                break;\n                            }\n\n                            // The remaining matchers can be distributed over the remaining parameters,\n                            // so we can use up this matcher:\n                            expressions[argumentIndex] = new MatchExpression(matches[matchIndex]);\n                            ++matchIndex;\n                        }\n                    }\n\n                    if (matchIndex < matches.Length)\n                    {\n                        // If we get here, we can be almost certain that matchers weren't distributed properly\n                        // across the invocation's parameters. We could hope for the best and just leave it\n                        // at that; however, it's probably better to let client code know, so it can be either\n                        // adjusted or reported to Moq.\n                        throw new ArgumentException(\n                            string.Format(\n                                CultureInfo.CurrentCulture,\n                                Resources.MatcherAssignmentFailedDuringExpressionReconstruction,\n                                matches.Length,\n                                $\"{invocation.Method.DeclaringType!.GetFormattedName()}.{invocation.Method.Name}\"));\n                    }\n\n                    bool CanDistribute(int msi, int asi)\n                    {\n                        var match = matches[msi];\n                        var matchType = match.RenderExpression.Type;\n                        for (int ai = asi; ai < expressions.Length; ++ai)\n                        {\n                            if (parameterTypes[ai].IsAssignableFrom(matchType)\n                                && CanDistribute(msi + 1, ai + 1))\n                            {\n                                return true;\n                            }","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/ActionObserver.cs#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make matcher types exactly match the parameter types (use It.IsAny<short>() for a short parameter, not It.IsAny<int>()).","Remove implicit conversions by casting explicitly so Moq can correlate matchers to positions.","Disambiguate the target overload (cast the lambda or use explicit delegate types) so the recorded invocation matches the declared method.","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."],"exampleFix":"// before\nmock.SetupAction(x => x.Write(It.IsAny<int>())); // parameter is short\n// after\nmock.SetupAction(x => x.Write(It.IsAny<short>()));","handlingStrategy":"validation","validationCode":"// Match It.IsAny<T> type arguments exactly to parameter types\nDebug.Assert(matcherTypes.Zip(paramTypes, (m, p) => m == p).All(x => x), \"Matcher/parameter type mismatch\");","typeGuard":"static bool MatchersAlign(MethodInfo method, Type[] matcherTypes) =>\n    method.GetParameters().Select(p => p.ParameterType).SequenceEqual(matcherTypes);","tryCatchPattern":"try\n{\n    mock.SetupAction(lambda);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"positions for all argument matchers\"))\n{\n    throw new InvalidOperationException(\"Matcher types must exactly match parameter types; otherwise report to Moq.\", ex);\n}","preventionTips":["Use It.IsAny<T>() with T exactly equal to the parameter type (no implicit conversions)","Avoid ambiguous overloads in setups; cast the lambda or delegate to pick the exact overload","Watch for boxing of value types when passing matchers","Treat this message as a probable Moq bug: build a minimal repro if types already match"],"tags":["moq","argument-matchers","type-conversion","bug"],"backgroundTag":"internal-invariant-violation","analyzedSha":"89a5be629c752960fe403e95ff583e4ae6f00542","analyzedAt":"2026-09-16T05:31:39.496Z","contentChangedAt":"2026-09-16T05:31:39.496Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}