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
- Construct every ParameterExpression with an explicit name: Expression.Parameter(typeof(int), "value").
- Audit manually-built expression trees for any ParameterExpression whose .Name is null before they reach EF precompilation.
- 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
- Always pass a name string to Expression.Parameter(type, name).
- When accepting expression trees from callers, validate parameter names up front.
- Unit-test expression builders against the translator to catch unnamed parameters early.
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
- Encountered non-quotable expression of type
- Missing default arm for switch expression
- Switch with non-null comparison method
- Unary node with non-null method
- Unsupported LINQ unary node
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)