devlooped/moq · error · ArgumentException

Delays have to be greater than zero to ensure an async…

Error message

Delays have to be greater than zero to ensure an async callback is used.

What it means

This ArgumentException from Guard.Positive enforces that a delay passed to Moq's async/timing APIs (e.g. Mock.Of/Delays inside SetupSequence or ReturnsAsync delay overloads, via Moq's Delay/Timeout features) is strictly greater than TimeSpan.Zero. A zero or negative delay cannot guarantee the async callback machinery is used.

Solutions

  1. Pass a strictly positive TimeSpan, e.g. TimeSpan.FromMilliseconds(1) or greater.
  2. Validate/normalize configured delay values before calling the API (clamp to a minimum of 1ms).
  3. If you truly need no delay, use the non-delayed overload of the API instead of passing zero.

Example fix

// before
mock.SetupSequence(m => m.Load()).ReturnsAsync(data, TimeSpan.Zero);
// after
mock.SetupSequence(m => m.Load()).ReturnsAsync(data, TimeSpan.FromMilliseconds(50));
Defensive patterns

Strategy: validation

Validate before calling

if (delay <= TimeSpan.Zero)
    throw new ArgumentException("Delay must be positive before calling Moq delay APIs");

Type guard

static bool IsPositiveDelay(TimeSpan d) => d > TimeSpan.Zero;

Try / catch

try { api.WithDelay(delay); }
catch (ArgumentException ex) when (ex.Message.Contains("Delays"))
{
    delay = TimeSpan.FromMilliseconds(1); // retry with minimal positive delay
}

Prevention

When it happens

Trigger: Passing TimeSpan.Zero or a negative TimeSpan to an API whose guard is Guard.Positive(TimeSpan), such as delay-based callback/Returns overloads used with async setups.

Common situations: Computing a delay from config that resolves to 0; sign errors making the TimeSpan negative; default TimeSpan values (TimeSpan.Zero) passed through unmodified.

Related errors


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

Appendix: source

Thrown at src/Moq/Guard.cs:223

                        memberAccess.ToStringFixed()));
        }

        public static void IsMockable(Type type)
        {
            if (!type.IsMockable())
            {
                throw new NotSupportedException(
                    string.Format(
                        Resources.TypeNotMockable,
                        type.GetFormattedName()));
            }
        }

        public static void Positive(TimeSpan delay)
        {
            if (delay <= TimeSpan.Zero)
            {
                throw new ArgumentException(Resources.DelaysMustBeGreaterThanZero);
            }
        }

        public static void CanRead(PropertyInfo property)
        {
            if (!property.CanRead(out _))
            {
                throw new ArgumentException(string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.PropertyGetNotFound,
                    property.DeclaringType!.Name, property.Name));
            }
        }

        public static void CanWrite(PropertyInfo property)
        {
            if (!property.CanWrite(out _))
            {

View on GitHub (pinned to 89a5be629c)