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
- 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.
- Pre-parse the JSON manually: read the property as string, normalize it (convert ISO-8601 durations via XmlConvert.ToTimeSpan), then deserialize.
- Catch JsonException around Deserialize and handle invalid duration values with a default.
- 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
- Always serialize TimeSpan with invariant culture formats
- Never accept ISO-8601 durations (PT1H) directly into TimeSpan-typed JSON properties
- Validate duration strings before persisting them to JSON documents
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
- Unexpected token parsing TimeSpan. Expected a string, got
- Cannot convert to TimeSpan
- Unknown token type
- Deserializing a is not supported.
- Unexpected token type
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)