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
- Guard with FindJsonElement (which returns null instead of throwing when the column has no underlying column) and skip unmapped properties.
- Ensure the JsonQueryExpression is constructed over a real column-backed ColumnExpression so JsonColumn.Column is non-null.
- If iterating properties, skip those with no JSON representation (shadow keys) before calling GetJsonElement.
- 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
- Prefer FindJsonElement (returns null) over GetJsonElement (throws) when columns may be synthetic.
- Construct JsonQueryExpression over real column-backed ColumnExpression.
- Skip properties with no JSON representation (shadow keys) before resolving.
- Provider authors: guard GetJsonElement against non-columnar JSON expansions.
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
- JsonElementMappingNotFound
- UnableToBindMemberToEntityProjection
- Cosmos-specific methods can only be used when the context is
- Unhandled expression '{expression}' of type '{expressionType
- Metadata model returned should not be 'null'. Provider: {pro
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/21312ee1540b671e.
Report an issue: GitHub.