devlooped/moq · error · InvalidOperationException

InvalidOperationException (unhandled Times kind in…

Error message

InvalidOperationException (unhandled Times kind in GetExceptionMessage)

What it means

Times.GetExceptionMessage(callCount) picks a resource string by Kind and throws InvalidOperationException when the kind is unrecognized. Like the ToString guard, it signals a Times value carrying an invalid Kind at the moment Moq formats a 'no matching calls' verification failure message.

Solutions

  1. Use only the public Times factories to create instances
  2. Unify Moq package versions across the solution to eliminate enum/value skew
  3. If unavoidable, wrap Verify calls in try-catch for InvalidOperationException to produce a diagnosable failure, and report the Moq bug

Example fix

// before
var times = default(Times); // Kind = 0 default may still be invalid if constructed raw
// after
var times = Times.AtLeastOnce();
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    mock.Verify(m => m.Foo(), times);
}
catch (InvalidOperationException ex)
{
    // invalid Times kind reached GetExceptionMessage; rebuild and retry
    times = Times.AtLeastOnce();
    mock.Verify(m => m.Foo(), times);
}

Prevention

When it happens

Trigger: A Times instance with an out-of-range Kind reaches verification-failure message formatting — via invalid construction, deserialization, or mixed Moq assembly versions.

Common situations: Same as the ToString invariant break: hand-built Times values, assembly version skew in the test runner, corrupted state.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at src/Moq/Times.cs:280

            if (this.kind == Kind.BetweenExclusive)
            {
                --from;
                ++to;
            }

            var message = this.kind switch
            {
                Kind.AtLeastOnce => Resources.NoMatchingCallsAtLeastOnce,
                Kind.AtLeast => Resources.NoMatchingCallsAtLeast,
                Kind.AtMost => Resources.NoMatchingCallsAtMost,
                Kind.AtMostOnce => Resources.NoMatchingCallsAtMostOnce,
                Kind.BetweenExclusive => Resources.NoMatchingCallsBetweenExclusive,
                Kind.BetweenInclusive => Resources.NoMatchingCallsBetweenInclusive,
                Kind.Exactly => Resources.NoMatchingCallsExactly,
                Kind.Once => Resources.NoMatchingCallsOnce,
                Kind.Never => Resources.NoMatchingCallsNever,
                _ => throw new InvalidOperationException(),
            };

            return string.Format(CultureInfo.CurrentCulture, message, from, to, callCount);
        }

        /// <summary>
        ///   Checks whether the specified number of invocations matches the constraint described by this instance.
        /// </summary>
        /// <param name="count">The number of invocations to check.</param>
        /// <returns>
        ///   <see langword="true"/> if <paramref name="count"/> matches the constraint described by this instance;
        ///   otherwise, <see langword="false"/>.
        /// </returns>
        public bool Validate(int count)
        {
            var (from, to) = this;
            return from <= count && count <= to;
        }

View on GitHub (pinned to 89a5be629c)