dotnet/efcore · error · InvalidOperationException
Complex property ' ' cannot have both a JSON column name ('…
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 throws ComplexPropertyBothJsonColumnAndJsonPropertyName when a complex property has both a JSON column mapping (the ContainerColumnName annotation, set by ToJson) and an explicit JSON property name (HasJsonPropertyName). These are mutually exclusive: ToJson makes the complex type its own JSON column, while HasJsonPropertyName designates a property within a containing JSON column. The validator rejects the ambiguous combination.
Solutions
- Choose one strategy: use ToJson() to map the complex type as its own JSON column, OR use HasJsonPropertyName() to map it inside an existing JSON column — not both.
- Remove the conflicting call: drop HasJsonPropertyName when ToJson is present, or drop ToJson when the complex property lives inside another JSON-mapped type.
- Audit the configuration chain for stale fluent calls left over from a refactor.
Example fix
// before — both configured
modelBuilder.Entity<Order>()
.OwnsOne(o => o.Details, d =>
{
d.ToJson("details");
d.HasJsonPropertyName("order_details"); // throws
});
// after — pick one (ToJson makes Details its own column)
modelBuilder.Entity<Order>()
.OwnsOne(o => o.Details, d => d.ToJson("details")); Defensive patterns
Strategy: validation
Validate before calling
var hasColumn = complexProperty.ComplexType
.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string;
var hasJsonName = complexProperty.GetJsonPropertyName() != null;
if (hasColumn && hasJsonName)
throw new InvalidOperationException("Complex property cannot have both JSON column and JSON property name."); Type guard
static bool HasConflictingJsonConfig(IComplexProperty cp)
=> cp.GetJsonPropertyName() != null
&& cp.ComplexType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string; Prevention
- Use either ToJson() or HasJsonPropertyName() on a complex property, never both.
- Audit fluent chains after refactors for stale configuration calls.
- Add a model-validation test asserting no complex property has both annotations.
When it happens
Trigger: Chaining .ToJson("col") and .HasJsonPropertyName("name") on the same complex property builder; or scaffolding that produced both annotations. The check reads ContainerColumnName from the annotation and compares with GetJsonPropertyName() result.
Common situations: Copy-pasting configuration between owned-type-as-JSON and complex-property-as-JSON-property; partial migration where ToJson was added but HasJsonPropertyName was retained; IDE auto-complete adding the wrong fluent call.
Related errors
- Complex property ' ' cannot use 'HasJsonPropertyName()'…
- Complex property ' ' is mapped to JSON but its containing…
- The complex collection property
- 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/964072a6fbd604e1.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)