dotnet/efcore · error · InvalidOperationException
Complex property ' ' is mapped to JSON but its containing…
Error message
Complex property '{complexProperty}' is mapped to JSON but its containing type '{containingType}' is not. Map the root complex type to JSON. See https://github.com/dotnet/efcore/issues/36558. What it means
ValidatePropertyMapping throws NestedComplexPropertyJsonWithTableSharing when a complex property is mapped to JSON (ComplexType.IsMappedToJson() is true) but its declaring type is itself a ComplexType that is NOT mapped to JSON. A JSON complex nested inside a non-JSON complex is ambiguous (the outer one is flattened to columns). Tracked as Issue #36558; the message points developers to map the root complex type to JSON.
Solutions
- Map the outermost (root) complex property to JSON with ToJson(), so the entire nested graph is JSON.
- Alternatively, remove ToJson from the inner complex property so the whole graph is flattened to columns.
- Ensure JSON mapping is consistent up the complex-property chain — either all JSON from the root or none.
Example fix
// before — outer Address flattened, inner Geo mapped to JSON
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.Address, a =>
{
a.OwnsOne(a2 => a2.Geo, g => g.ToJson("geo")); // throws
});
// after — map the root complex to JSON
modelBuilder.Entity<Customer>()
.OwnsOne(c => c.Address, a =>
{
a.ToJson("address");
a.OwnsOne(a2 => a2.Geo, g => { /* inherits JSON mapping */ });
}); Defensive patterns
Strategy: validation
Validate before calling
if (complexProperty.ComplexType.IsMappedToJson()
&& !complexProperty.DeclaringType.IsMappedToJson()
&& complexProperty.DeclaringType is IComplexType)
throw new InvalidOperationException("Inner complex is JSON but outer complex is not; map the root to JSON."); Type guard
static bool HasInconsistentNestedJsonMapping(IComplexProperty cp)
=> cp.ComplexType.IsMappedToJson()
&& !cp.DeclaringType.IsMappedToJson()
&& cp.DeclaringType is IComplexType; Prevention
- Apply ToJson() at the root complex property when nested complexes use JSON.
- Keep JSON mapping consistent up the complex-property chain — all or none.
- Validate nested complex graphs at model finalization in a unit test.
When it happens
Trigger: A nested complex property where the inner complex is configured with ToJson but the outer (containing) complex is not — i.e. outer flattens to columns while inner wants JSON. The check is `complexProperty.ComplexType.IsMappedToJson() && !DeclaringType.IsMappedToJson() && DeclaringType is IComplexType`.
Common situations: Adding ToJson only on the inner complex of a nested value-object graph; refactoring where the outer ToJson was removed but the inner kept; scaffolding that inconsistently applies JSON mapping across nesting levels.
Related errors
- Complex property ' ' cannot have both a JSON column name ('…
- Complex property ' ' cannot use 'HasJsonPropertyName()'…
- 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/ed8e622c8fd90983.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:307
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)
{
// Issue #36558
throw new InvalidOperationException(
RelationalStrings.NestedComplexPropertyJsonWithTableSharing(
$"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}",
complexProperty.DeclaringType.DisplayName()));
}
ValidateJsonProperties(complexProperty.ComplexType);
}
}
/// <summary>
/// Validates the SQL query mapping for an entity type.
/// </summary>
/// <param name="entityType">The entity type to validate.</param>
/// <param name="logger">The logger to use.</param>
protected virtual void ValidateSqlQuery(
IEntityType entityType,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{View on GitHub (pinned to 3a2006ef56)