JamesNK/Newtonsoft.Json · error · ArgumentException

Object must be of type TimeSpan.

Error message

Object must be of type TimeSpan.

What it means

Thrown by JValue.CompareTo when the token's JSON type is JTokenType.TimeSpan and the operand is not a TimeSpan. TimeSpan JValues are compared with TimeSpan.CompareTo; a non-TimeSpan operand is rejected. This is the last of the four type-strict primitive comparisons (Bytes/Guid/Uri/TimeSpan) inside CompareTo.

Source

Thrown at Src/Newtonsoft.Json/Linq/JValue.cs:394

                    Guid guid1 = (Guid)objA;
                    Guid guid2 = (Guid)objB;

                    return guid1.CompareTo(guid2);
                case JTokenType.Uri:
                    Uri? uri2 = objB as Uri;
                    if (uri2 == null)
                    {
                        throw new ArgumentException("Object must be of type Uri.");
                    }

                    Uri uri1 = (Uri)objA;

                    return Comparer<string>.Default.Compare(uri1.ToString(), uri2.ToString());
                case JTokenType.TimeSpan:
                    if (!(objB is TimeSpan))
                    {
                        throw new ArgumentException("Object must be of type TimeSpan.");
                    }

                    TimeSpan ts1 = (TimeSpan)objA;
                    TimeSpan ts2 = (TimeSpan)objB;

                    return ts1.CompareTo(ts2);
                default:
                    throw MiscellaneousUtils.CreateArgumentOutOfRangeException(nameof(valueType), valueType, "Unexpected value type: {0}".FormatWith(CultureInfo.InvariantCulture, valueType));
            }
        }

        private static int CompareFloat(object objA, object objB)
        {
            double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture);
            double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture);

            // take into account possible floating point errors
            if (MathUtils.ApproxEquals(d1, d2))

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Parse the operand to TimeSpan before comparing: token.CompareTo(TimeSpan.Parse(text)).
  2. Compare the underlying values: token.Value<TimeSpan>() vs TimeSpan.FromSeconds(n).
  3. Type-check the operand before invoking CompareTo.

Example fix

// before
int c = tsToken.CompareTo("00:05:00");

// after
int c = tsToken.CompareTo(TimeSpan.Parse("00:05:00"));
Defensive patterns

Strategy: type-guard

Validate before calling

if (other is TimeSpan ts) { int c = token.CompareTo(ts); } else if (other is string s) { int c = token.CompareTo(TimeSpan.Parse(s)); }

Type guard

static bool IsTimeSpan(object? o) => o is TimeSpan;

Try / catch

try { int c = token.CompareTo(other); } catch (ArgumentException ex) when (ex.Message.Contains("TimeSpan")) { /* parse string to TimeSpan */ }

Prevention

When it happens

Trigger: Comparing a TimeSpan JValue against a string such as token.CompareTo("00:01:00") or an integer number of ticks. Triggered during sorting/equality on token collections that contain duration fields.

Common situations: Duration/delay fields deserialized as TimeSpan then compared to string or numeric literals; mixing TimeSpan and string duration representations.

Related errors


AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07). Data as JSON: /api/errors/3a2b1e159a38d88a. Report an issue: GitHub.