dotnet/efcore · error · InvalidOperationException

An error occurred while reading a database value. The expect

Error message

An error occurred while reading a database value. The expected type was '{expectedType}' but the actual value was of type '{actualType}'.

What it means

Thrown by ThrowReadValueException when the data reader returned a value whose runtime type cannot be cast to the expected CLR type, surfaced as an InvalidCastException wrapper. Fires in the property-less else branch — i.e. a projected value, not an entity property. The message reports both the expected type (the generic TValue) and the actual runtime type returned by the driver.

Source

Thrown at src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs:2048

            message
                = exception is NullReferenceException
                || Equals(value, DBNull.Value)
                    ? RelationalStrings.ErrorMaterializingPropertyNullReference(entityType, propertyName, expectedType)
                    : exception is InvalidCastException
                        ? CoreStrings.ErrorMaterializingPropertyInvalidCast(entityType, propertyName, expectedType, actualType)
                        : RelationalStrings.ErrorMaterializingProperty(entityType, propertyName);
        }
        else
        {
            message
                = exception is NullReferenceException
                    ? RelationalStrings.ErrorMaterializingValueNullReference(expectedType)
                    : exception is InvalidCastException
                        ? RelationalStrings.ErrorMaterializingValueInvalidCast(expectedType, actualType)
                        : RelationalStrings.ErrorMaterializingValue;
        }

        throw new InvalidOperationException(message, exception);
    }

    /// <summary>
    ///     Gets the value of JSON property name used for the given property of an entity mapped to a JSON column.
    /// </summary>
    /// <remarks>
    ///     Unless configured explicitly, entity property name is used.
    /// </remarks>
    /// <param name="property">The property.</param>
    /// <returns>
    ///     The value for the JSON property used to store the value of this entity property.
    ///     By default <see langword="null" /> is returned for key properties and for properties that
    ///     are not mapped to JSON.
    /// </returns>
    public static string? GetJsonPropertyName(this IReadOnlyProperty property)
        => (string?)property.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value
            ?? (property.IsKey() || !property.DeclaringType.IsMappedToJson()
                ? null

View on GitHub (pinned to 3a2006ef56)

Solutions

  1. Align the projected CLR type with the column's mapped type — query property.GetColumnType() or sp_help to confirm.
  2. Add a HasConversion on the source property, or coerce in the projection (e.g. b.Id.ToString()).
  3. For FromSqlRaw, verify the SELECT column order and types match the DTO field order and types.
  4. Upgrade the provider package in lockstep with EF Core so type mappings stay consistent.

Example fix

// before
var ids = await db.Database.SqlQueryRaw<Guid>("SELECT token FROM tokens").ToListAsync();

// after — token column is varchar
var ids = await db.Database.SqlQueryRaw<string>("SELECT token FROM tokens").ToListAsync();
Defensive patterns

Strategy: validation

Validate before calling

// confirm the column type matches TValue before projecting
var columnType = property.GetColumnType();
if (!typeof(TValue).IsAssignableFrom(map.GetClrType(columnType)))
    throw new InvalidOperationException($"Type mismatch: {columnType} -> {typeof(TValue)}");

Type guard

static bool CanMapTo(Type clrType, string storeType, IRelationalTypeMappingSource src)
    => src.FindMapping(clrType, storeType) is not null;

Try / catch

try { var v = await query.ToListAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("actual value was of type"))
{
    logger.LogError(ex, "Type mismatch during materialization; inner: {Inner}", ex.InnerException?.Message);
    throw;
}

Prevention

When it happens

Trigger: Materializing a projected value where the database column type differs from the CLR type and the ADO.NET driver cannot convert (e.g. SQL column is VARCHAR but TValue is Guid; or a JSON column returns string while TValue is JObject). Distinct from the property-keyed variant because no IProperty is bound at the throw site.

Common situations: Raw SQL projections with FromSqlRaw into a non-entity type whose column order/types drifted from the DTO; manual DbDataReader usage bridged into EF; provider version upgrades that changed type mapping defaults (e.g. byte[] vs string); misconfigured value converters on projections.

Related errors


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