dotnet/efcore · error · InvalidOperationException
The complex collection property
Error message
The complex collection property '{entityType}.{property}' must be mapped to a JSON column. Use 'ToJson()' to configure this complex collection as mapped to a JSON column. What it means
ValidatePropertyMapping throws ComplexCollectionNotMappedToJson when a complex property is a collection (complexProperty.IsCollection) but its ComplexType is not mapped to JSON. Relational providers require complex collections to live in a JSON column because there is no general multi-row flattening for complex types; only JSON carries the array semantics.
Solutions
- Add .ToJson("column_name") to the OwnsMany configuration for the complex collection.
- If you actually want relational one-to-many with a child table, use OwnsMany with a navigation that the model treats as an entity collection (HasOne/HasMany) instead of a complex collection.
- Verify the property is genuinely a complex collection (List<ComplexType>) and not an entity collection before applying ToJson.
Example fix
// before
modelBuilder.Entity<Order>()
.OwnsMany(o => o.Items, i =>
{
i.Property(p => p.Sku).HasMaxLength(50);
}); // throws — no ToJson
// after
modelBuilder.Entity<Order>()
.OwnsMany(o => o.Items, i =>
{
i.ToJson("items");
i.Property(p => p.Sku).HasMaxLength(50);
}); Defensive patterns
Strategy: validation
Validate before calling
if (complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson())
throw new InvalidOperationException("Complex collection must be mapped to JSON; add ToJson()."); Type guard
static bool IsComplexCollectionMappedToJson(IComplexProperty cp)
=> !cp.IsCollection || cp.ComplexType.IsMappedToJson(); Prevention
- Always chain .ToJson() when configuring OwnsMany for complex collections.
- Distinguish complex collections (need JSON) from entity one-to-many (child table).
- Add a model-finalization unit test to catch missing ToJson early.
When it happens
Trigger: Calling OwnsMany (or a complex collection property) without ToJson on the owning configuration. The check is `complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson()`.
Common situations: Migrating from owned collection (one-to-many) to complex collection and forgetting ToJson; scaffolding a complex collection then customizing it without JSON; assuming OwnsMany maps to a child table like before EF9 complex types.
Related errors
- Complex property ' ' cannot have both a JSON column name ('…
- Complex property ' ' cannot use 'HasJsonPropertyName()'…
- Complex property ' ' is mapped to JSON but its containing…
- The key on the entity type ' ' cannot be configured because…
- Both properties ' ' and ' ' on entity type ' ' are mapped…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/c8ab7b36f918d98e.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:269
if (property is { IsPrimitiveCollection: true }
&& property.GetTypeMapping().ElementTypeMapping?.ElementTypeMapping != null)
{
throw new InvalidOperationException(
RelationalStrings.NestedCollectionsNotSupported(
property.ClrType.ShortDisplayName(), property.DeclaringType.DisplayName(), property.Name));
}
}
/// <inheritdoc />
protected override void ValidatePropertyMapping(
IComplexProperty complexProperty,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
base.ValidatePropertyMapping(complexProperty, logger);
if (complexProperty.IsCollection && !complexProperty.ComplexType.IsMappedToJson())
{
throw new InvalidOperationException(
RelationalStrings.ComplexCollectionNotMappedToJson(
complexProperty.DeclaringType.DisplayName(), complexProperty.Name));
}
if (!complexProperty.ComplexType.IsMappedToJson()
&& complexProperty.IsNullable
&& complexProperty.ComplexType.GetProperties().All(m => m.IsNullable))
{
throw new InvalidOperationException(
RelationalStrings.ComplexPropertyOptionalTableSharing(complexProperty.ComplexType.DisplayName(), complexProperty.Name));
}
if (complexProperty.GetJsonPropertyName() != null)
{
if (complexProperty.ComplexType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName)
{
throw new InvalidOperationException(
RelationalStrings.ComplexPropertyBothJsonColumnAndJsonPropertyName(View on GitHub (pinned to 3a2006ef56)