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

  1. Guard the query: skip the Contains predicate (or short-circuit to an empty result) when the collection is empty.
  2. Materialize the collection to a non-empty placeholder or pass the list as a parameter that EF handles via parameterized IN expansion.
  3. Upgrade EF Core; the inline-VALUES path is progressively better at short-circuiting empty inputs across releases.
  4. 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

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


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