dotnet/wpf · error · ArgumentException

SR.Timing_InvalidArgNonNegative

Error message

SR.Timing_InvalidArgNonNegative

What it means

The Duration(TimeSpan) constructor creates a duration used by WPF animation timing. Negative TimeSpans are meaningless as durations, so the constructor throws ArgumentException ("Property value must be greater than or equal to zero or indefinite.") when timeSpan < TimeSpan.Zero.

Solutions

  1. Clamp computed TimeSpans to TimeSpan.Zero before constructing a Duration.
  2. Use Duration.Forever or Duration.Automatic for indefinite/special semantics instead of a negative value.
  3. Validate configuration inputs so negative durations are rejected or coerced at parse time.

Example fix

// before
var duration = new Duration(endTime - DateTime.Now); // negative if past
// after
var remaining = endTime - DateTime.Now;
var duration = new Duration(remaining > TimeSpan.Zero ? remaining : TimeSpan.Zero);
Defensive patterns

Strategy: validation

Validate before calling

if (timeSpan < TimeSpan.Zero) timeSpan = TimeSpan.Zero;

Type guard

static bool IsNonNegative(TimeSpan t) => t >= TimeSpan.Zero;

Try / catch

try { var d = new Duration(ts); }
catch (ArgumentException ex) when (ex.Message.Contains("greater than or equal to zero")) { var d = Duration.Forever; }

Prevention

When it happens

Trigger: new Duration(TimeSpan.FromMilliseconds(-1)) or any negative TimeSpan, commonly produced by subtracting TimeSpans or computing a remaining-time that underflows.

Common situations: Computing animation durations from deadlines that already passed (endTime - now < 0); converting signed durations from other systems; configuration parsing yielding negative values.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/7b4d78c4e45e0158. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Duration.cs:26

{
    /// <summary>
    /// Duration struct provides sentinel values that are not included in TimeSpan.
    /// This structure may represent a TimeSpan, Automatic, or Forever value.
    /// </summary>
    [TypeConverter(typeof(DurationConverter))]
    public readonly struct Duration
    {
        private readonly TimeSpan _timeSpan;
        private readonly DurationType _durationType;

        /// <summary>
        /// Creates a Duration from a TimeSpan.
        /// </summary>
        /// <param name="timeSpan"></param>
        public Duration(TimeSpan timeSpan)
        {
            if (timeSpan < TimeSpan.Zero)
                throw new ArgumentException(SR.Timing_InvalidArgNonNegative, nameof(timeSpan));

            _durationType = DurationType.TimeSpan;
            _timeSpan = timeSpan;
        }

        /// <summary>
        /// Private constructor, server for creation of <see cref="Duration.Automatic"/> and <see cref="Duration.Forever"/> only.
        /// </summary>
        /// <param name="durationType">Only <see cref="Duration.Automatic"/> and <see cref="Duration.Forever"/> values are permitted.</param>
        private Duration(DurationType durationType)
        {
            Debug.Assert(durationType == DurationType.Automatic || durationType == DurationType.Forever);

            _durationType = durationType;
        }

        #region Operators

View on GitHub (pinned to 81131a70a4)