dotnet/efcore · error · InvalidOperationException

Invalid token type: ' '.

Error message

Invalid token type: '{tokenType}'.

What it means

CosmosJsonNumberProjectionReaderWriter<T>.FromJsonTyped reads a numeric projection from a JSON token. It calls Utf8JsonReader.TryGetDouble; if that returns false the token is not a number (e.g. String, Null, True, PropertyName). EF then throws CoreStrings.JsonReaderInvalidTokenType with the actual token type, because it expected to materialize a numeric value.

Solutions

  1. Correct the stored JSON so the property is emitted as a JSON number (and not null) for that document.
  2. Make the property nullable in the model if nulls are legitimately possible, so a different reader/writer handles null.
  3. Audit documents that violate the schema (e.g. with a Cosmos query for items where IS_NUMBER(prop) = false) and repair them.

Example fix

// before - stored document
{ "score": "42" }   // string token, TryGetDouble fails

// after
{ "score": 42 }    // numeric token
// or, in the model, allow null:
public int? Score { get; set; }
Defensive patterns

Strategy: validation

Validate before calling

// Before reading, verify stored values are numeric for projected properties
// (run a one-off Cosmos query)
// SELECT c.id FROM c WHERE NOT IS_NUMBER(c.score)
// Repair any documents returned before running the EF query.

Type guard

// When materializing from arbitrary JSON, narrow before mapping
static bool IsNumericToken(ref Utf8JsonReader r)
    => r.TokenType == JsonTokenType.Number
       || (r.TokenType == JsonTokenType.String
           && double.TryParse(r.GetString(), out _));

Try / catch

try { return await dbContext.Set<Item>().ToListAsync(ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Invalid token type"))
{
    // run a Cosmos query to find documents where the numeric prop is not a number,
    // repair them, then retry the read.
}

Prevention

When it happens

Trigger: Reading a Cosmos document where a property mapped as a numeric projection (int/long/float/etc.) contains a non-numeric JSON value. TryGetDouble fails for tokens like Null, String, Boolean, or when the reader is positioned on a PropertyName rather than a value. See CosmosJsonNumberProjectionReaderWriter.cs:35.

Common situations: Storing data outside EF (or with a different model) so that a numeric column ends up as a JSON string or null. Schema drift between model and stored documents. Migrating data where numbers were serialized as strings. A null in a non-nullable numeric field.

Understand the failure class

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/1af626c52fa808c9. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Cosmos/Storage/Internal/CosmosJsonNumberProjectionReaderWriter.cs:35

///     Projections of numbers in cosmos can result in double precision floating point numbers,
///     and thus have to be read as doubles to prevent reader exceptions
/// </remarks>
public sealed class CosmosJsonNumberProjectionReaderWriter<T> : JsonValueReaderWriter<T>
    where T : INumber<T>
{
    private static readonly PropertyInfo
        InstanceProperty = typeof(CosmosJsonNumberProjectionReaderWriter<T>).GetProperty(nameof(Instance))!;

    /// <summary>
    ///     The singleton instance of this stateless reader/writer.
    /// </summary>
    public static CosmosJsonNumberProjectionReaderWriter<T> Instance { get; } = new();

    /// <inheritdoc />
    public override T FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
        => manager.CurrentReader.TryGetDouble(out var d)
            ? T.CreateChecked(d) // #38138
            : throw new InvalidOperationException(CoreStrings.JsonReaderInvalidTokenType(manager.CurrentReader.TokenType));

    /// <inheritdoc />
    public override void ToJsonTyped(Utf8JsonWriter writer, T value)
    {
        if (typeof(T) == typeof(int)
            || typeof(T) == typeof(short)
            || typeof(T) == typeof(sbyte)
            || typeof(T) == typeof(byte)
            || typeof(T) == typeof(ushort))
        {
            writer.WriteNumberValue(int.CreateChecked(value));
        }
        else if (typeof(T) == typeof(uint))
        {
            writer.WriteNumberValue(uint.CreateChecked(value));
        }
        else if (typeof(T) == typeof(long))
        {

View on GitHub (pinned to 3a2006ef56)