JamesNK/Newtonsoft.Json · error · ArgumentException

Can not convert {0} to TimeSpan.

Error message

Can not convert {0} to TimeSpan.

What it means

Thrown by the explicit cast operator 'operator TimeSpan(JToken)' (NON-nullable) when the token is not a TimeSpan-compatible JValue. ArgumentException {0} is the resolved JTokenType; allowed types are TimeSpanTypes ({String, Comment, Raw, TimeSpan}) — the narrowest set. Non-nullable overload, so a JSON null token ALSO throws. Only a TimeSpan or a culture-invariant TimeSpan string is accepted.

Source

Thrown at Src/Newtonsoft.Json/Linq/JToken.cs:1479

            if (v.Value is byte[] bytes)
            {
                return new Guid(bytes);
            }

            return (v.Value is Guid guid) ? guid : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)!);
        }

        /// <summary>
        /// Performs an explicit conversion from <see cref="JToken"/> to <see cref="TimeSpan"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static explicit operator TimeSpan(JToken value)
        {
            JValue? v = EnsureValue(value);
            if (v == null || !ValidateToken(v, TimeSpanTypes, false))
            {
                throw new ArgumentException("Can not convert {0} to TimeSpan.".FormatWith(CultureInfo.InvariantCulture, GetType(value)));
            }

            return (v.Value is TimeSpan span) ? span : ConvertUtils.ParseTimeSpan(Convert.ToString(v.Value, CultureInfo.InvariantCulture)!);
        }

        /// <summary>
        /// Performs an explicit conversion from <see cref="JToken"/> to <see cref="Nullable{T}"/> of <see cref="TimeSpan"/>.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static explicit operator TimeSpan?(JToken? value)
        {
            if (value == null)
            {
                return null;
            }

            JValue? v = EnsureValue(value);

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Use the Nullable overload (TimeSpan?)token["duration"] so JSON null returns null.
  2. If the value is numeric (ticks/millis), convert it yourself (TimeSpan.FromMilliseconds(...)) instead of casting.
  3. Guard token["duration"]?.Type is String or TimeSpan before the non-nullable cast, and use TimeSpan.TryParse.
  4. Normalize the producer to emit standard TimeSpan strings like "00:01:30".

Example fix

// before
TimeSpan duration = (TimeSpan)payload["duration"]; // throws on integer millis

// after
TimeSpan duration = TimeSpan.TryParse((string?)payload["duration"], out var ts) ? ts : TimeSpan.Zero;
Defensive patterns

Strategy: validation

Validate before calling

TimeSpan duration = TimeSpan.TryParse((string?)payload["duration"], out var ts) ? ts : TimeSpan.Zero;

Type guard

static bool IsTimeSpanLike(JToken? t) =>
    t is JValue v && (v.Type == JTokenType.TimeSpan || v.Type == JTokenType.String);

Try / catch

TimeSpan duration;
try { duration = (TimeSpan)payload["duration"]; }
catch (ArgumentException) { duration = TimeSpan.Zero; }

Prevention

When it happens

Trigger: Calling (TimeSpan)token["duration"] when the JValue Type is Integer, Float, Boolean, Date, Guid, Uri, Bytes, Null, Object, or Array, or the node is a JObject/JArray (EnsureValue null). An epoch-integer duration or a number is rejected because TimeSpan parsing from numbers is ambiguous.

Common situations: Duration field emitted as an integer (ticks/millis/seconds) instead of a TimeSpan string; null duration in some records cast to non-nullable TimeSpan; field changed to an ISO duration object.

Related errors


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