dotnet/efcore · error · InvalidOperationException

An error occurred while reading a database value. The…

Error message

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

What it means

Thrown by ThrowReadValueException when the underlying data reader returned a null/DBNull for a value slot whose target CLR type is a non-nullable value type, and the shaper has no IProperty context (the else branch). EF wraps the NullReferenceException or DBNull-equivalent into an InvalidOperationException with a clear expected-type-vs-null message. It indicates the database row contained NULL where the materialization contract expected a value.

Solutions

  1. Make the projected CLR member nullable (int? / DateTime?) so DBNull maps cleanly.
  2. If reading an entity, mark the property as nullable in the model (IsRequired(false)) or add a NOT NULL default constraint.
  3. Update the database schema to disallow NULL and backfill existing NULL rows.
  4. For DTO projections, coerce in the selector: b.SomeIntColumn ?? 0.

Example fix

// before
var dtos = await db.Blogs
    .Select(b => new BlogDto { Rank = b.Rank })  // Rank is int, column nullable
    .ToListAsync();

// after
public class BlogDto { public int? Rank { get; set; } }
Defensive patterns

Strategy: validation

Validate before calling

// before materializing, ensure projected members are nullable when the column is nullable
var rows = await db.Blogs
    .Select(b => new BlogDto { Rank = (int?)b.Rank })
    .ToListAsync();

Type guard

// narrow value-type results defensively
static T? SafeRead<T>(object? v) where T : struct => v is DBNull or null ? null : (T)v;

Try / catch

try { var v = await query.FirstAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("actual value was 'null'"))
{
    logger.LogWarning(ex, "Null in non-nullable projection; check schema/DTO");
    throw;
}

Prevention

When it happens

Trigger: A DbDataReader slot yielded DBNull (or a NullReferenceException) during materialization of a projected value with no associated IProperty — typically inside a non-entity projection (e.g. a Select(b => new Dto { Count = b.SomeInt }) where the underlying column is null). The else branch fires because property is null.

Common situations: Database column is nullable but the projection CLR type is non-nullable int/DateTime/Guid; a LEFT JOIN produced NULL for the right side; default constraints were dropped so newly inserted rows have NULL where EF expects a value; views with outer joins materialized into value-type DTOs.

Related errors


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

Appendix: 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)