dotnet/efcore · error · InvalidOperationException
Complex property '{complexProperty}' cannot have both a JSON
Error message
Complex property '{complexProperty}' cannot have both a JSON column name ('{columnName}') and a JSON property name ('{propertyName}') configured. Use 'ToJson()' to map to a JSON column or 'HasJsonPropertyName()' to map as a JSON property within a containing JSON column, but not both. What it means
ValidatePropertyMapping (RelationalModelExtensions.cs:282-291) throws when a complex property simultaneously has a JSON column name annotation (ContainerColumnName, set via ToJson/HasContainerColumnName) AND a JSON property name annotation (JsonPropertyName, set via HasJsonPropertyName). These describe mutually exclusive mappings: ToJson maps the property to its own JSON column, HasJsonPropertyName maps it as a key inside an enclosing JSON column. Specifying both is contradictory.
Source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:286
{
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(
$"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}",
columnName,
complexProperty.GetJsonPropertyName()));
}
if (!complexProperty.DeclaringType.IsMappedToJson())
{
throw new InvalidOperationException(
RelationalStrings.ComplexPropertyJsonPropertyNameWithoutJsonMapping(
$"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}"));
}
}
if (complexProperty.ComplexType.IsMappedToJson())
{
if (!complexProperty.DeclaringType.IsMappedToJson()
&& complexProperty.DeclaringType is IComplexType)View on GitHub (pinned to dbf9771522)
Solutions
- If the complex property should be its own JSON column, keep ToJson() and remove HasJsonPropertyName().
- If the complex property should be a JSON key inside an owning JSON column, keep HasJsonPropertyName() and remove ToJson()/HasContainerColumnName(), ensuring the containing type is JSON-mapped.
- Audit the configuration chain so only one JSON mapping intent is expressed.
Example fix
// before - both mappings
modelBuilder.Entity<Order>().OwnsOne(o => o.Billing, b =>
{
b.ToJson("BillingJson");
b.HasJsonPropertyName("bill"); // throws: cannot have both
});
// after - pick one (own JSON column)
modelBuilder.Entity<Order>().OwnsOne(o => o.Billing, b =>
{
b.ToJson("BillingJson");
});
// OR (nested inside another JSON column)
// b.HasJsonPropertyName("bill"); Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in context.Model.GetEntityTypes())
{
foreach (var cp in et.GetComplexProperties())
{
bool hasColumnName = cp.ComplexType
.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string;
bool hasJsonProperty = cp.GetJsonPropertyName() != null;
if (hasColumnName && hasJsonProperty)
{
// will throw - keep only one JSON mapping intent.
}
}
} Prevention
- Never call both ToJson()/HasContainerColumnName and HasJsonPropertyName on the same property.
- Decide JSON-column vs JSON-key mapping per complex property and apply only that API.
- Audit JSON config after refactoring complex types.
When it happens
Trigger: Calling both ToJson("col") (or HasContainerColumnName) and HasJsonPropertyName("key") on the same complex property. Thrown at model validation.
Common situations: Copy-pasting JSON config; refactoring a complex type from a standalone JSON column into a nested property but leaving the old ToJson() call in place.
Related errors
- The property '{property}' on type '{type}' is mapped to a JS
- The key {keyProperties} on the entity type '{entityType}' ca
- The optional complex property '{type}.{property}' is mapped
- Complex property '{complexProperty}' cannot use 'HasJsonProp
- Complex property '{complexProperty}' is mapped to JSON but i
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/964072a6fbd604e1.
Report an issue: GitHub.