dotnet/efcore · error · InvalidOperationException

Empty collections are not supported as inline query roots.

Error message

Empty collections are not supported as inline query roots.

What it means

GenerateValues refuses to render a VALUES inline query root that contains zero rows. EF translates certain in-memory collections used as query roots into a VALUES construct; an empty collection has no columns/types to project, so SQL generation is impossible. The check is hit at SQL-generation time, meaning the query already passed translation with an empty row set.

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 3a2006ef56)

Solutions

  1. Short-circuit before the query when the collection is empty (return an empty result or skip the predicate/join).
  2. For Contains filters, build the predicate conditionally so EF never inlines an empty VALUES: if (ids.Count > 0) query = query.Where(e => ids.Contains(e.Id));
  3. Avoid using empty in-memory sequences as query roots; materialize the server side first, then join in memory.

Example fix

// before
var q = from e in db.Entities
        from id in ids.AsQueryable()   // ids may be empty -> 585
        select new { e, id };
// after
if (ids.Count == 0) return Array.Empty<Result>();
var q = from e in db.Entities
        from id in ids.AsQueryable()
        select new { e, id };
Defensive patterns

Strategy: validation

Validate before calling

// Never hand EF an empty inline collection as a query root / Contains source.
if (ids is null || ids.Count == 0)
    return Array.Empty<Result>();

var q = db.Entities.Where(e => ids.Contains(e.Id));

// For an in-memory root join:
if (memoryList.Count == 0) return Enumerable.Empty<Result>();

Prevention

When it happens

Trigger: Using an empty in-memory collection as a query root in a composed query that EF translates server-side, e.g. from e in db.Entities from id in emptyArray.AsQueryable() select e, or Contains over a parameterized empty list that EF chose to inline as VALUES instead of a parameter. Also seen with open-ended composition where a sub-source resolves empty at execution.

Common situations: Dynamic filters where a list can be empty (e.g. var ids = FilterIds(); ... where ids.Contains(e.Id)); joining against an in-memory list that happens to be empty for a given request; unit/integration tests passing empty collections.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/347bfdce39d2e7e7. Report an issue: GitHub.