dotnet/efcore · error · ArgumentException
SelectExpression can only be built over a JsonQueryExpressio
Error message
SelectExpression can only be built over a JsonQueryExpression that represents a collection within the JSON document.
What it means
CreateSelect over a JsonQueryExpression requires the JSON entity to be a collection (a JSON array). This ArgumentException fires when the JsonQueryExpression.IsCollection is false, i.e. someone tried to build a SelectExpression over a single (scalar/entity) JSON value. Single-valued JSON owned entities are accessed as columns through their owner, not as independent query roots.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs:855
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
protected virtual SelectExpression CreateSelect(
JsonQueryExpression jsonQueryExpression,
TableExpressionBase tableExpressionBase,
string identifierColumnName,
Type identifierColumnType,
RelationalTypeMapping identifierColumnTypeMapping)
{
if (!jsonQueryExpression.IsCollection)
{
throw new ArgumentException(RelationalStrings.SelectCanOnlyBeBuiltOnCollectionJsonQuery, nameof(jsonQueryExpression));
}
var structuralType = jsonQueryExpression.StructuralType;
var jsonColumn = jsonQueryExpression.JsonColumn;
var tableAlias = tableExpressionBase.Alias!;
Check.DebugAssert(
structuralType.BaseType is null && !structuralType.GetDirectlyDerivedTypes().Any(),
"Inheritance encountered inside a JSON document");
// Create a dictionary mapping all properties to their ColumnExpressions, for the SelectExpression's projection.
var propertyExpressions = new Dictionary<IProperty, ColumnExpression>();
foreach (var property in structuralType.GetPropertiesInHierarchy())
{
// For owned JSON mapping, add column(s) representing key of the parent (non-JSON) entity, on top of all the projections from OPENJSON/json_each/etc.
if (jsonQueryExpression.KeyPropertyMap?.TryGetValue(property, out var ownerKeyColumn) == true)
{
propertyExpressions[property] = ownerKeyColumn;View on GitHub (pinned to 3a2006ef56)
Solutions
- Access a single-valued JSON owned entity through its owner: project owner.Owned.Property rather than querying the owned entity as a root.
- If you need to query a collection inside JSON, map it with OwnsMany(...).ToJson(...) so IsCollection is true.
- Do not call .AsQueryable()/LINQ operators that turn a single-valued JSON navigation into a SelectExpression root.
Example fix
// before (single-valued JSON owned treated as queryable)
modelBuilder.Entity<Order>().OwnsOne(o => o.Address, a => a.ToJson());
var q = db.Orders.SelectMany(o => new[] { o.Address }.AsQueryable());
// after (project through owner)
var q = db.Orders.Select(o => new { o.Id, o.Address.City }); Defensive patterns
Strategy: validation
Validate before calling
// Do not turn a single-valued JSON owned entity into a query root.
// OwnsOne(...).ToJson() => scalar/entity (not collection); access via the owner.
// OwnsMany(...).ToJson() => collection; safe to compose.
var owneds = db.Model.FindEntityType(typeof(Owner))!
.GetNavigations().Select(n => n.TargetEntityType)
.Where(t => t.IsOwned() && t.IsMappedToJson());
foreach (var owned in owneds)
if (!owned.FindOwnership()!.IsUnique)
Console.WriteLine($"{owned.Name}: JSON collection (queryable)");
else
Console.WriteLine($"{owned.Name}: JSON scalar/entity (access via owner)"); Prevention
- Access single-valued JSON owned entities via their owner projection.
- Reserve .AsQueryable() over JSON for OwnsMany(...).ToJson() collections.
- Document which owned navigations are JSON collections vs. single objects.
When it happens
Trigger: Internal/advanced usage that constructs a JsonQueryExpression for a non-collection JSON owned entity and then tries to build a SelectExpression over it. End-user-reachable through APIs that expose JSON-mapped owned entities as queryable when the owned entity is mapped as a single object (.ToJson() on OwnsOne) rather than a collection (.ToJson() on OwnsMany).
Common situations: Calling .AsQueryable() on a reference (non-collection) JSON-mapped owned navigation and trying to compose; third-party extensions that build JSON queries incorrectly; misconfiguring OwnsOne with ToJson and treating the result as a queryable collection.
Related errors
- This node should be handled by provider-specific SQL generat
- '{operation}' used over owned type '{entityType}' which is m
- The LINQ expression '{expression}' could not be translated.
- Unable to translate set operation after client projection ha
- 'DefaultIfEmpty' cannot be applied after a client-evaluated
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/b8a7374500ef838f.
Report an issue: GitHub.