OrchardCMS/OrchardCore · error · JsonException

Unable to convert ' ' to TimeSpan.

Error message

Unable to convert '{stringValue}' to TimeSpan.

What it means

TimeSpanJsonConverter.Read parses a JSON string into a TimeSpan using TimeSpan.TryParse. If the string is not in a recognizable time-span format (invariant culture), the converter throws JsonException, which surfaces during JsonSerializer.Deserialize as a JsonException wrapping the message.

Solutions

  1. Store the value in a format TimeSpan.Parse accepts invariantly (e.g. '1.02:03:04', '01:30:00') and re-serialize the offending document.
  2. Pre-parse the JSON manually: read the property as string, normalize it (convert ISO-8601 durations via XmlConvert.ToTimeSpan), then deserialize.
  3. Catch JsonException around Deserialize and handle invalid duration values with a default.
  4. If round-tripping across cultures, use XmlConvert.ToString/ToTimeSpan on a string property instead of relying on the converter.

Example fix

// before
var options = new TimeSpan("1 hour"); // JSON: "1 hour"
// after
// JSON: "1.00:00:00" or normalize before deserializing
var raw = (string?)json["duration"];
var ts = TimeSpan.TryParse(raw, CultureInfo.InvariantCulture, out var v) ? v : TimeSpan.Zero;
Defensive patterns

Strategy: validation

Validate before calling

if (!TimeSpan.TryParse(raw, CultureInfo.InvariantCulture, out var ts)) throw new FormatException($"Invalid TimeSpan value: {raw}");

Type guard

static bool IsValidTimeSpan(string? s) => TimeSpan.TryParse(s, CultureInfo.InvariantCulture, out _);

Try / catch

try { var v = JsonSerializer.Deserialize<MyDto>(json); }
catch (JsonException ex) when (ex.Message.Contains("to TimeSpan")) { /* use default / report invalid duration */ }

Prevention

When it happens

Trigger: Deserializing a JSON property typed TimeSpan whose value string cannot be parsed, e.g. '"duration": "1 hour"', '"duration": ""', or a culture-specific format like '1,2:30' not valid invariantly. Also thrown when a non-string JSON token (number/null) is read because the reader tries GetString conversion paths.

Common situations: Reading persisted settings or content-item JSON written by another system or locale, hand-edited appsettings values like '90 minutes', API payloads from non-.NET clients using ISO-8601 durations ('PT1H') which TimeSpan.TryParse does not accept, or upgrading serialized data where the format changed.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/ec1bc1c2bcd68f0a. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/TimeSpanJsonConverter.cs:24

public class TimeSpanJsonConverter : JsonConverter<TimeSpan>
{
    public static readonly TimeSpanJsonConverter Instance = new();

    public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType != JsonTokenType.String)
        {
            throw new JsonException($"Unexpected token parsing TimeSpan. Expected a string, got '{reader.TokenType}'.");
        }

        var stringValue = reader.GetString();

        if (TimeSpan.TryParse(stringValue, out var timeSpan))
        {
            return timeSpan;
        }

        throw new JsonException($"Unable to convert '{stringValue}' to TimeSpan.");
    }

    public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options)
        => writer.WriteStringValue(value.ToString());
}

View on GitHub (pinned to 4306c0717f)