dotnet/BenchmarkDotNet · error · JsonException
Unexpected token {reader.TokenType} when parsing double.
Error message
Unexpected token {reader.TokenType} when parsing double. What it means
Thrown by SimpleJsonDoubleConverter.Read during JSON deserialization when the current token is not a JsonTokenType.Number. Similar to the float converter, the Write method encodes NaN/PositiveInfinity/NegativeInfinity as empty strings, but Read rejects all non-number tokens. Additionally, the Read method has a latent bug: it calls reader.GetSingle() (returning float) instead of reader.GetDouble(), which truncates precision on valid double values — this is a separate correctness issue beyond the exception itself.
Source
Thrown at src/BenchmarkDotNet/Serialization/BdnSimpleJsonSerializer.cs:75
{
if (float.IsNaN(value) || float.IsInfinity(value))
writer.WriteStringValue("");
else
writer.WriteNumberValue(value);
}
}
/// <summary>
/// Custom JsonConverter for double that write Nan/PositiveInfinite/NegativeInfinite as empty string.
/// </summary>
private class SimpleJsonDoubleConverter : JsonConverter<double>
{
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
return reader.GetSingle();
throw new JsonException($"Unexpected token {reader.TokenType} when parsing double.");
}
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
if (double.IsNaN(value) || double.IsInfinity(value))
writer.WriteStringValue("");
else
writer.WriteNumberValue(value);
}
}
}
View on GitHub (pinned to b515068b61)
Solutions
- For round-trip support, the Read method needs to handle string tokens (including empty string for NaN/Infinity) — this requires a library-level fix or custom converter.
- Ensure the input JSON encodes doubles as JSON numbers, not strings.
- Be aware of the GetSingle() vs GetDouble() precision bug: even when deserialization succeeds, doubles may lose precision. Report or patch this if high-precision values matter.
- Prefer the default System.Text.Json double handling for interop if BdnSimpleJsonSerializer's custom behavior is not required.
Example fix
// Current Read has two issues:
// 1. Calls GetSingle() instead of GetDouble() (precision loss)
// 2. Rejects string tokens that Write emits for NaN/Infinity
// A corrected Read:
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
return reader.GetDouble(); // fix: was GetSingle()
if (reader.TokenType == JsonTokenType.String)
{
var s = reader.GetString();
return string.IsNullOrEmpty(s) ? double.NaN : double.Parse(s);
}
throw new JsonException($"Unexpected token {reader.TokenType} when parsing double.");
} Defensive patterns
Strategy: validation
Validate before calling
// Before deserializing, check for string-encoded doubles in the JSON
using var doc = JsonDocument.Parse(jsonString);
foreach (var prop in doc.RootElement.EnumerateObject())
if (prop.Value.ValueKind == JsonValueKind.String && prop.Name.Contains("double", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("JSON contains string-encoded double values that cannot be deserialized by BdnSimpleJsonSerializer."); Try / catch
try
{
var result = JsonSerializer.Deserialize<MyModel>(json, BdnSimpleJsonSerializer.Options);
}
catch (JsonException ex) when (ex.Message.Contains("parsing double"))
{
// The double was serialized as a string (NaN/Infinity). Use default System.Text.Json options instead.
var result = JsonSerializer.Deserialize<MyModel>(json);
} Prevention
- For round-tripping JSON with doubles, prefer System.Text.Json's default options over BdnSimpleJsonSerializer.
- Filter NaN/Infinity values from data before serialization if re-import is required.
- Be aware of the precision bug: BdnSimpleJsonSerializer's double Read calls GetSingle(), truncating precision — report or patch if needed.
- Run a round-trip test (serialize then deserialize) to catch converter asymmetries before production use.
When it happens
Trigger: Deserializing JSON with double fields encoded as strings, null tokens, or the empty strings produced by Write for NaN/Infinity. The asymmetry between Write (emits empty string) and Read (rejects non-number) means JSON exported with this serializer cannot be safely re-imported if it contains NaN/Infinity.
Common situations: Exporting benchmark statistics (which frequently contain NaN for standard deviation of single-sample runs or Infinity in ratio calculations) to JSON and re-importing. Reading externally-produced JSON with string-encoded doubles.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13).
Data as JSON: /api/errors/3e432c007948fe29.
Report an issue: GitHub.