dotnet/efcore · error · InvalidOperationException
The JSON property name should only be configured on nested…
Error message
The JSON property name should only be configured on nested owned navigations.
What it means
HasJsonPropertyName configures the JSON property name for an owned navigation. EF Core requires the navigation's PrincipalEntityType to itself be owned (IsOwned()), because JSON property names are only meaningful on nested owned navigations within a JSON column. Configuring it on a top-level or non-owned navigation throws.
Solutions
- Ensure the principal entity type is owned (OwnsOne / OwnsMany) before setting the JSON property name.
- Move the HasJsonPropertyName call into the nested owned-navigation configuration chain.
- Configure the JSON column mapping (ToJson) on the outermost owned navigation first.
Example fix
// before
modelBuilder.Entity<Order>().Navigation(o => o.Details).HasJsonPropertyName("details");
// after
modelBuilder.Entity<Order>().OwnsOne(o => o.Details, nb => nb.ToJson("details").HasJsonPropertyName("details")); Defensive patterns
Strategy: validation
Validate before calling
// Ensure the principal entity type is owned before configuring JSON property name.
if (!navigationBuilder.Metadata.PrincipalEntityType.IsOwned())
{
// configure ownership (OwnsOne/OwnsMany) first, or skip HasJsonPropertyName.
return;
}
navigationBuilder.HasJsonPropertyName(name); Type guard
static bool IsNestedOwnedNavigation(OwnedNavigationBuilder nb)
=> nb.Metadata.PrincipalEntityType.IsOwned(); Try / catch
try { navigationBuilder.HasJsonPropertyName(name); }
catch (InvalidOperationException ex) when (ex.Message.Contains("JSON property name"))
{ /* principal not owned; configure OwnsOne/ToJson first */ } Prevention
- Always configure OwnsOne/OwnsMany and ToJson before HasJsonPropertyName.
- Keep JSON property name config inside the nested owned-navigation builder chain.
- Write a model test asserting HasJsonPropertyName calls are on owned types.
When it happens
Trigger: Calling navigationBuilder.HasJsonPropertyName(name) where navigationBuilder.Metadata.PrincipalEntityType.IsOwned() returns false — i.e. the containing entity is not an owned type participating in a JSON mapping.
Common situations: Calling HasJsonPropertyName on a top-level entity's navigation instead of a nested owned one; configuring JSON mapping before marking the type as owned; refactoring ownership without revisiting JSON config.
Related errors
- Both properties ' ' and ' ' on entity type ' ' are mapped…
- Default value ' ' of type ' ' cannot be set on property ' '…
- A call was made to ' ' that changed an option that must be…
- A call was made to ' ' that changed an option that must be…
- A full-text index is defined for
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/3495b0ed40208fac.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs:132
return builder;
}
/// <summary>
/// Configures the navigation of an entity mapped to a JSON column, mapping the navigation to a specific JSON property,
/// rather than using the navigation name.
/// </summary>
/// <param name="navigationBuilder">The builder for the navigation being configured.</param>
/// <param name="name">JSON property name to be used.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public static OwnedNavigationBuilder HasJsonPropertyName(
this OwnedNavigationBuilder navigationBuilder,
string? name)
{
Check.NullButNotEmpty(name);
if (!navigationBuilder.Metadata.PrincipalEntityType.IsOwned())
{
throw new InvalidOperationException(
RelationalStrings.JsonPropertyNameShouldBeConfiguredOnNestedNavigation);
}
navigationBuilder.Metadata.DeclaringEntityType.SetJsonPropertyName(name);
return navigationBuilder;
}
/// <summary>
/// Configures the navigation of an entity mapped to a JSON column, mapping the navigation to a specific JSON property,
/// rather than using the navigation name.
/// </summary>
/// <param name="navigationBuilder">The builder for the navigation being configured.</param>
/// <param name="name">JSON property name to be used.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public static OwnedNavigationBuilder<TSource, TTarget> HasJsonPropertyName<TSource, TTarget>(
this OwnedNavigationBuilder<TSource, TTarget> navigationBuilder,
string? name)View on GitHub (pinned to 3a2006ef56)