dotnet/efcore · error · NotSupportedException

Unnamed captured variable

Error message

Unnamed captured variable

What it means

Thrown by the LINQ-to-C# expression-tree translator when it visits a ParameterExpression that is captured from outside the entire expression tree (not declared in any in-scope lambda) but whose Name property is null. Because the translator must emit the variable by its name into generated C#, an unnamed captured parameter cannot be represented. This lives in the precompiled-query / compiled-model translation pipeline that turns query expression trees back into source code.

Solutions

  1. Construct every ParameterExpression with an explicit name: Expression.Parameter(typeof(int), "value").
  2. Audit manually-built expression trees for any ParameterExpression whose .Name is null before they reach EF precompilation.
  3. If the unnamed parameter originates from a library, update or replace that library so it names its parameters.

Example fix

// before
var p = Expression.Parameter(typeof(int));
// after
var p = Expression.Parameter(typeof(int), "value");
Defensive patterns

Strategy: validation

Validate before calling

// Walk the tree and fail fast on unnamed captured parameters before translation
bool HasUnnamedParameters(Expression e)
{
    return new ParameterChecker().Visit(e).Any();
}

class ParameterChecker : ExpressionVisitor
{
    readonly List<ParameterExpression> _bad = new();
    protected override Expression VisitParameter(ParameterExpression p)
    {
        if (p.Name is null) _bad.Add(p);
        return base.VisitParameter(p);
    }
}

Type guard

// Narrow to a safe-to-translate parameter
static bool IsTranslatableParameter(ParameterExpression p) => !string.IsNullOrEmpty(p.Name);

Try / catch

// Wrap translation; surface a clear message
try { var csharp = translator.Translate(expr); }
catch (NotSupportedException ex) when (ex.Message.Contains("Unnamed captured variable"))
{ throw new InvalidOperationException("Query contains an unnamed captured parameter; name all ParameterExpression instances.", ex); }

Prevention

When it happens

Trigger: VisitParameter resolves a ParameterExpression that is absent from both the current scope's variable map and the lifted-state variable map (so it is treated as externally captured), and that ParameterExpression was constructed without a name argument (e.g. Expression.Parameter(typeof(int))).

Common situations: A query or value-generator expression is built programmatically with Expression.Parameter(type) omitting the name; a third-party extension hands EF an expression tree containing unnamed parameters; precompiled queries process a captured closure variable whose ParameterExpression lacks a name.

Related errors


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

Appendix: source

Thrown at src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs:2166

    protected override Expression VisitParameter(ParameterExpression parameter)
    {
        // Note that the parameter in the lambda declaration is handled separately in VisitLambda
        if (_stack.Peek().Variables.TryGetValue(parameter, out var name)
            || _liftedState.Variables.TryGetValue(parameter, out name))
        {
            Result = IdentifierName(name);

            return parameter;
        }

        // This parameter is unknown to us - it's captured from outside the entire expression tree.
        // Simply return its name without worrying about uniquification, since the variable needs to correspond to the outside in any
        // case (it's the callers responsibility).
        _capturedVariables.Add(parameter);

        if (parameter.Name is null)
        {
            throw new NotSupportedException("Unnamed captured variable");
        }

        Result = IdentifierName(parameter.Name);
        return parameter;
    }

    /// <inheritdoc />
    protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
        => throw new NotSupportedException();

    /// <inheritdoc />
    protected override SwitchCase VisitSwitchCase(SwitchCase node)
        => throw new NotSupportedException("Translation happens as part of VisitSwitch");

    /// <inheritdoc />
    protected override Expression VisitSwitch(SwitchExpression switchNode)
    {
        Result = TranslateSwitch(switchNode, lowerableAssignmentVariable: null);

View on GitHub (pinned to 3a2006ef56)