dotnet/efcore · error · InvalidOperationException

Complex property '{complexProperty}' is mapped to JSON but i

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 (RelationalModelValidator.cs:301-311, tracking issue #36558) throws when a complex property is mapped to JSON but its declaring type is itself a complex type that is NOT mapped to JSON. A JSON column must be rooted at an entity (table); a complex type that flattens to columns cannot host a JSON sub-document. The whole JSON branch must be rooted in JSON, so EF rejects nested JSON inside a table-shared complex type.

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 dbf9771522)

Solutions

  1. Move the ToJson() up to the root complex property so the entire JSON subtree (entity root -> complex -> nested complex) is consistently JSON-mapped.
  2. If the outer complex must stay table-mapped, map the inner collection/entity to its own table or as owned entities rather than a nested JSON column.
  3. Restructure so only the entity-rooted complex type carries ToJson().

Example fix

// before - JSON nested inside a table-shared complex type
modelBuilder.Entity<Customer>().ComplexProperty(c => c.Contact, contact =>
{
    // Contact is flattened to columns (no ToJson)
    contact.OwnsOne(c => c.Preferences, prefs =>
    {
        prefs.ToJson("PrefsJson"); // throws: parent not JSON
    });
});

// after - root the whole JSON subtree at the entity
modelBuilder.Entity<Customer>().ComplexProperty(c => c.Contact, contact =>
{
    contact.ToJson("ContactJson");
    contact.OwnsOne(c => c.Preferences, prefs => { });
});
Defensive patterns

Strategy: validation

Validate before calling

foreach (var et in context.Model.GetEntityTypes())
{
    foreach (var cp in et.GetComplexProperties())
    {
        if (cp.ComplexType.IsMappedToJson()
            && !cp.DeclaringType.IsMappedToJson()
            && cp.DeclaringType is IComplexType)
        {
            // will throw (#36558) - move ToJson() to the root complex property.
        }
    }
}

Prevention

When it happens

Trigger: A nested complex property (declaring type is a complex type) configured with ToJson() while the outer complex type is mapped to columns (no ToJson on the root). Thrown at model validation.

Common situations: Marking only an inner complex property as JSON during a partial migration to JSON columns; building nested value-object structures and applying ToJson at the wrong level.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/ed8e622c8fd90983. Report an issue: GitHub.