dotnet/efcore · error · InvalidOperationException
EmptyCollectionNotSupportedAsInlineQueryRoot
EmptyCollectionNotSupportedAsInlineQueryRoot
Error message
Empty collections are not supported as inline query roots.
What it means
Thrown by GenerateValues when a ValuesExpression (the inline VALUES construct EF builds for parameterized collections) has zero rows. EF inlines small client collections as a VALUES query root (e.g. for Contains), but an empty VALUES clause cannot be emitted as a standalone query root, so it is rejected.
Source
Thrown at src/EFCore.Relational/Query/QuerySqlGenerator.cs:1620
_relationalCommandBuilder
.Append(")")
.Append(AliasSeparator)
.Append(_sqlGenerationHelper.DelimitIdentifier(valuesExpression.Alias));
return valuesExpression;
}
/// <summary>
/// Generates SQL for a VALUES expression.
/// </summary>
/// <param name="valuesExpression">The <see cref="ValuesExpression" /> for which to generate SQL.</param>
protected virtual void GenerateValues(ValuesExpression valuesExpression)
{
var rowValues = valuesExpression switch
{
{ RowValues.Count: 0 }
=> throw new InvalidOperationException(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot),
{ ValuesParameter: not null }
=> throw new UnreachableException(
"ValuesExpression.ValuesParameter has to be expanded to constants before SQL generation (i.e. in SqlNullabilityProcessor)"),
{ RowValues: not null } => valuesExpression.RowValues,
_ => throw new UnreachableException()
};
// Some databases support providing the names of columns projected out of VALUES, e.g.
// SQL Server/PG: (VALUES (1, 3), (2, 4)) AS x(a, b). Others unfortunately don't; so by default, we extract out the first row,
// and generate a SELECT for it with the names, and a UNION ALL over the rest of the values.
_relationalCommandBuilder.Append("SELECT ");
Check.DebugAssert(rowValues.Count > 0);
var firstRowValues = rowValues[0].Values;
for (var i = 0; i < firstRowValues.Count; i++)View on GitHub (pinned to dbf9771522)
Solutions
- Guard the query: skip the Contains predicate (or short-circuit to an empty result) when the collection is empty.
- Materialize the collection to a non-empty placeholder or pass the list as a parameter that EF handles via parameterized IN expansion.
- Upgrade EF Core; the inline-VALUES path is progressively better at short-circuiting empty inputs across releases.
- If authoring a provider, ensure empty collections are translated to a universally-false predicate rather than an inline VALUES root.
Example fix
// before
var ids = new List<int>();
var posts = db.Posts.Where(p => ids.Contains(p.Id)).ToList(); // may hit inline empty VALUES
// after
var ids = new List<int>();
var posts = ids.Count == 0
? new List<Post>()
: db.Posts.Where(p => ids.Contains(p.Id)).ToList(); Defensive patterns
Strategy: validation
Validate before calling
IReadOnlyList<int> ids = GetIds(); if (ids.Count == 0) return Array.Empty<Post>(); return db.Posts.Where(p => ids.Contains(p.Id)).ToList();
Try / catch
try { return db.Posts.Where(p => ids.Contains(p.Id)).ToList(); }
catch (InvalidOperationException ex) when (ids.Count == 0 && ex.Message.Contains("Empty collections"))
{ return Array.Empty<Post>(); } Prevention
- Short-circuit queries when the input collection is empty.
- Prefer Any/All over Contains when the collection may be empty.
- Upgrade EF Core to benefit from improved empty-collection short-circuiting.
- If authoring a provider, lower empty Contains to 'WHERE 1=0' rather than inline VALUES.
When it happens
Trigger: A LINQ query that forces a collection into an inline query root while that collection is empty - typically db.Set<T>().Where(e => someEmptyList.Contains(e.Id)) where the provider decides to inline the empty list rather than expand it to a false predicate. Also any manual construction that yields a ValuesExpression with RowValues.Count == 0.
Common situations: Dynamic filters where an optional list happens to be empty at runtime; EF version differences in how empty Contains collections are lowered; provider-specific behavior that routes to inline VALUES rather than the standard 'WHERE 1=0' short-circuit.
Related errors
- The property '{propertyType} {structuralType}.{property}' ha
- Cannot translate the '{comparisonOperator}' on an expression
- Cannot translate '{comparisonOperator}' on a subquery expres
- The expression '{sqlExpression}' in the SQL tree does not ha
- Unsupported operator '{nodeType}' specified for expression o
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/0e9cd18696383e7a.
Report an issue: GitHub.