{"record":{"id":"21312ee1540b671e","repo":"dotnet/efcore","slug":"jsonqueryexpressionwithoutunderlyingcolumn","errorCode":"JsonQueryExpressionWithoutUnderlyingColumn","errorMessage":"The JSON query expression for '{structuralType}' has no underlying column.","messagePattern":"The JSON query expression for '(.+?)' has no underlying column\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Query/JsonQueryExpression.cs","lineNumber":310,"sourceCode":"        return Update(jsonColumn, newKeyPropertyMap);\n    }\n\n    /// <summary>\n    ///     Finds the <see cref=\"IRelationalJsonElement\" /> for the given property/navigation/complex property within the\n    ///     JSON column referenced by this expression by matching <see cref=\"JsonColumn\" />'s underlying\n    ///     <see cref=\"ColumnExpression.Column\" /> against <see cref=\"IRelationalJsonElement.ContainingColumn\" />. This\n    ///     disambiguates entity-splitting, TPT and TPC scenarios where the same property has multiple JSON element\n    ///     mappings — one per concrete table.\n    ///     <see cref=\"IRelationalJsonElement.PropertyName\" /> may be <see langword=\"null\" /> for shadow keys that have\n    ///     no JSON representation; callers iterating over <see cref=\"ITypeBase.GetProperties\" /> must handle that case\n    ///     and skip them.\n    /// </summary>\n    /// <param name=\"propertyBase\">The property, navigation or complex property to look up.</param>\n    /// <returns>The JSON element mapping for <paramref name=\"propertyBase\" />.</returns>\n    public virtual IRelationalJsonElement GetJsonElement(IPropertyBase propertyBase)\n    {\n        var column = JsonColumn.Column\n            ?? throw new InvalidOperationException(\n                RelationalStrings.JsonQueryExpressionWithoutUnderlyingColumn(StructuralType.DisplayName()));\n        return FindJsonElement(propertyBase)\n            ?? throw new InvalidOperationException(\n                RelationalStrings.JsonElementMappingNotFound(propertyBase.DeclaringType.DisplayName(), propertyBase.Name, column.Name));\n    }\n\n    /// <summary>\n    ///     Finds the <see cref=\"IRelationalJsonElement\" /> for the given property/navigation/complex property within the\n    ///     JSON column referenced by this expression by matching <see cref=\"JsonColumn\" />'s underlying\n    ///     <see cref=\"ColumnExpression.Column\" /> against <see cref=\"IRelationalJsonElement.ContainingColumn\" />, or returns\n    ///     <see langword=\"null\" /> if no such element exists (including when <see cref=\"JsonColumn\" /> has no underlying\n    ///     <see cref=\"ColumnExpression.Column\" />, e.g. for synthetic JSON expansions over OPENJSON / json_each, or for\n    ///     iterated properties such as shadow keys that have no JSON representation).\n    /// </summary>\n    /// <param name=\"propertyBase\">The property, navigation or complex property to look up.</param>\n    /// <returns>The JSON element mapping for <paramref name=\"propertyBase\" />, or <see langword=\"null\" /> if not found.</returns>\n    public virtual IRelationalJsonElement? FindJsonElement(IPropertyBase propertyBase)\n    {","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Query/JsonQueryExpression.cs#L292-L328","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before - GetJsonElement throws when JsonColumn.Column is null\nforeach (var prop in structuralType.GetProperties()) {\n    var element = jsonExpr.GetJsonElement(prop); // throws if no underlying column\n}\n\n// after - use the null-safe lookup and skip unmapped properties\nforeach (var prop in structuralType.GetProperties()) {\n    var element = jsonExpr.FindJsonElement(prop);\n    if (element is null) continue; // shadow keys / synthetic columns: skip\n    // use element.PropertyName ...\n}","handlingStrategy":"validation","validationCode":"// Use FindJsonElement (null-safe) before GetJsonElement; skip unmapped properties.\nforeach (var prop in structuralType.GetProperties()) {\n    var element = jsonExpr.FindJsonElement(prop);\n    if (element is null) continue; // shadow keys / synthetic columns\n    // build JSON path from element.PropertyName ...\n}","typeGuard":"bool HasUnderlyingColumn(JsonQueryExpression e) => e.JsonColumn.Column is not null;","tryCatchPattern":null,"preventionTips":["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."],"tags":["ef-core","json","query-expression","provider","column-mapping"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}