dotnet/efcore · error · InvalidOperationException

JsonQueryExpressionWithoutUnderlyingColumn

JsonQueryExpressionWithoutUnderlyingColumn

Error message

The JSON query expression for '{structuralType}' has no underlying column.

What it means

Thrown by JsonQueryExpression.GetJsonElement (JsonQueryExpression.cs:310) when JsonColumn.Column is null. GetJsonElement resolves the JSON path for a property by matching the column's underlying store Column object; if the JSON column expression has no underlying column (e.g. a synthetic JSON expansion over OPENJSON / json_each), there is nothing to resolve against, so EF cannot map the property and throws.

Source

Thrown at src/EFCore.Relational/Query/JsonQueryExpression.cs:310

        return Update(jsonColumn, newKeyPropertyMap);
    }

    /// <summary>
    ///     Finds the <see cref="IRelationalJsonElement" /> for the given property/navigation/complex property within the
    ///     JSON column referenced by this expression by matching <see cref="JsonColumn" />'s underlying
    ///     <see cref="ColumnExpression.Column" /> against <see cref="IRelationalJsonElement.ContainingColumn" />. This
    ///     disambiguates entity-splitting, TPT and TPC scenarios where the same property has multiple JSON element
    ///     mappings — one per concrete table.
    ///     <see cref="IRelationalJsonElement.PropertyName" /> may be <see langword="null" /> for shadow keys that have
    ///     no JSON representation; callers iterating over <see cref="ITypeBase.GetProperties" /> must handle that case
    ///     and skip them.
    /// </summary>
    /// <param name="propertyBase">The property, navigation or complex property to look up.</param>
    /// <returns>The JSON element mapping for <paramref name="propertyBase" />.</returns>
    public virtual IRelationalJsonElement GetJsonElement(IPropertyBase propertyBase)
    {
        var column = JsonColumn.Column
            ?? throw new InvalidOperationException(
                RelationalStrings.JsonQueryExpressionWithoutUnderlyingColumn(StructuralType.DisplayName()));
        return FindJsonElement(propertyBase)
            ?? throw new InvalidOperationException(
                RelationalStrings.JsonElementMappingNotFound(propertyBase.DeclaringType.DisplayName(), propertyBase.Name, column.Name));
    }

    /// <summary>
    ///     Finds the <see cref="IRelationalJsonElement" /> for the given property/navigation/complex property within the
    ///     JSON column referenced by this expression by matching <see cref="JsonColumn" />'s underlying
    ///     <see cref="ColumnExpression.Column" /> against <see cref="IRelationalJsonElement.ContainingColumn" />, or returns
    ///     <see langword="null" /> if no such element exists (including when <see cref="JsonColumn" /> has no underlying
    ///     <see cref="ColumnExpression.Column" />, e.g. for synthetic JSON expansions over OPENJSON / json_each, or for
    ///     iterated properties such as shadow keys that have no JSON representation).
    /// </summary>
    /// <param name="propertyBase">The property, navigation or complex property to look up.</param>
    /// <returns>The JSON element mapping for <paramref name="propertyBase" />, or <see langword="null" /> if not found.</returns>
    public virtual IRelationalJsonElement? FindJsonElement(IPropertyBase propertyBase)
    {

View on GitHub (pinned to dbf9771522)

Solutions

  1. Guard with FindJsonElement (which returns null instead of throwing when the column has no underlying column) and skip unmapped properties.
  2. Ensure the JsonQueryExpression is constructed over a real column-backed ColumnExpression so JsonColumn.Column is non-null.
  3. If iterating properties, skip those with no JSON representation (shadow keys) before calling GetJsonElement.
  4. Verify the JSON column mapping is attached to an actual table column.

Example fix

// before - GetJsonElement throws when JsonColumn.Column is null
foreach (var prop in structuralType.GetProperties()) {
    var element = jsonExpr.GetJsonElement(prop); // throws if no underlying column
}

// after - use the null-safe lookup and skip unmapped properties
foreach (var prop in structuralType.GetProperties()) {
    var element = jsonExpr.FindJsonElement(prop);
    if (element is null) continue; // shadow keys / synthetic columns: skip
    // use element.PropertyName ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Use FindJsonElement (null-safe) before GetJsonElement; skip unmapped properties.
foreach (var prop in structuralType.GetProperties()) {
    var element = jsonExpr.FindJsonElement(prop);
    if (element is null) continue; // shadow keys / synthetic columns
    // build JSON path from element.PropertyName ...
}

Type guard

bool HasUnderlyingColumn(JsonQueryExpression e) => e.JsonColumn.Column is not null;

Prevention

When it happens

Trigger: Calling GetJsonElement on a JsonQueryExpression whose JsonColumn has no underlying ColumnExpression.Column. Encountered in providers or scenarios that build synthetic JSON expansions (OPENJSON/json_each) or when a JSON expression is constructed over a non-columnar source.

Common situations: Provider code emitting synthetic JSON expansions; custom JSON query-tree construction; edge cases in JSON translation where the column was projected away; bugs where GetJsonElement is called instead of the null-safe FindJsonElement.

Related errors


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