devlooped/moq · error · ArgumentOutOfRangeException

ArgumentOutOfRangeException: callCount

Error message

ArgumentOutOfRangeException: callCount ('{0}' value must be greater than or equal to '0').

What it means

AtMost builds the upper bound of an invocation-count constraint used in Verify, so a negative count is meaningless and would invert the allowed range (0..callCount), silently breaking verification; the factory rejects it eagerly at construction. The throw site is a plain argument-validation guard: the faulty input is an int callCount argument less than zero, e.g. mock.Verify(m => m.Foo(), Times.AtMost(-1)), often from a computed count that went below zero. Note this site throws the plain ArgumentOutOfRangeException(nameof(callCount)); the richer formatted message ('{0}' value must be greater than or equal to '0') belongs to a different constructor overload in the same type.

Solutions

  1. Validate/clamp the count to >= 0 before calling AtMost
  2. Replace sentinel -1 with a proper optional/nullable representation and choose Times accordingly
  3. Use Times.AtMost(0) or Times.Never for zero expected calls

Example fix

// before
mock.Verify(m => m.Foo(), Times.AtMost(limit)); // limit default -1
// after
mock.Verify(m => m.Foo(), limit < 0 ? Times.Never : Times.AtMost(limit));
Defensive patterns

Strategy: validation

Validate before calling

if (maxCalls >= 0)
    mock.Verify(m => m.Foo(), Times.AtMost(maxCalls));

Prevention

When it happens

Trigger: Calling Times.AtMost with a negative number, e.g. from an off-by-one subtraction, a default of -1 meaning 'unset', or user-supplied config.

Common situations: Uninitialized integer defaults (-1) fed into AtMost; subtracting counts; parsing configuration where sentinel negatives leak through.

Related errors


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

Appendix: source

Thrown at src/Moq/Times.cs:90

        ///   Specifies that a mocked method should be invoked one time as minimum.
        /// </summary>
        /// <returns>An object defining the allowed number of invocations.</returns>
        public static Times AtLeastOnce()
        {
            return new Times(Kind.AtLeastOnce, 1, int.MaxValue);
        }

        /// <summary>
        ///   Specifies that a mocked method should be invoked <paramref name="callCount"/> times
        ///   as maximum.
        /// </summary>
        /// <param name="callCount">The maximum number of times.</param>
        /// <returns>An object defining the allowed number of invocations.</returns>
        public static Times AtMost(int callCount)
        {
            if (callCount < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(callCount));
            }

            return new Times(Kind.AtMost, 0, callCount);
        }

        /// <summary>
        ///   Specifies that a mocked method should be invoked one time as maximum.
        /// </summary>
        /// <returns>An object defining the allowed number of invocations.</returns>
        public static Times AtMostOnce()
        {
            return new Times(Kind.AtMostOnce, 0, 1);
        }

        /// <summary>
        ///   Specifies that a mocked method should be invoked between
        ///   <paramref name="callCountFrom"/> and <paramref name="callCountTo"/> times.
        /// </summary>

View on GitHub (pinned to 89a5be629c)