{"record":{"id":"9abce4c9d6edb7f2","repo":"dotnet/efcore","slug":"unnamed-captured-variable","errorCode":null,"errorMessage":"Unnamed captured variable","messagePattern":"Unnamed captured variable","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs","lineNumber":2166,"sourceCode":"    protected override Expression VisitParameter(ParameterExpression parameter)\n    {\n        // Note that the parameter in the lambda declaration is handled separately in VisitLambda\n        if (_stack.Peek().Variables.TryGetValue(parameter, out var name)\n            || _liftedState.Variables.TryGetValue(parameter, out name))\n        {\n            Result = IdentifierName(name);\n\n            return parameter;\n        }\n\n        // This parameter is unknown to us - it's captured from outside the entire expression tree.\n        // Simply return its name without worrying about uniquification, since the variable needs to correspond to the outside in any\n        // case (it's the callers responsibility).\n        _capturedVariables.Add(parameter);\n\n        if (parameter.Name is null)\n        {\n            throw new NotSupportedException(\"Unnamed captured variable\");\n        }\n\n        Result = IdentifierName(parameter.Name);\n        return parameter;\n    }\n\n    /// <inheritdoc />\n    protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)\n        => throw new NotSupportedException();\n\n    /// <inheritdoc />\n    protected override SwitchCase VisitSwitchCase(SwitchCase node)\n        => throw new NotSupportedException(\"Translation happens as part of VisitSwitch\");\n\n    /// <inheritdoc />\n    protected override Expression VisitSwitch(SwitchExpression switchNode)\n    {\n        Result = TranslateSwitch(switchNode, lowerableAssignmentVariable: null);","sourceCodeStart":2148,"sourceCodeEnd":2184,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs#L2148-L2184","documentation":"When a ParameterExpression is referenced from outside the translated tree (a captured variable), VisitParameter emits it by its Name, which must match the caller's variable. If parameter.Name is null there is no valid identifier to emit, so it throws NotSupportedException. Compiler-generated captured parameters always have names; null names come from Expression.Parameter(type) with no name.","triggerScenarios":"A lambda closes over a ParameterExpression created via Expression.Parameter(type) (no name argument) that the translator then treats as captured.","commonSituations":"Programmatic expression-tree building with unnamed parameters that get referenced across tree boundaries; dynamic query composition that stitches together subtrees.","solutions":["Always name parameters: Expression.Parameter(type, \"name\").","Avoid closure capture of unnamed parameters in precompiled queries.","Inline the captured value as a constant instead of referencing an unnamed parameter."],"exampleFix":"// before\nvar p = Expression.Parameter(typeof(int)); // Name is null -> throws when captured\n// after\nvar p = Expression.Parameter(typeof(int), \"threshold\");","handlingStrategy":"validation","validationCode":"public sealed class UnnamedParamDetector : ExpressionVisitor {\n    public bool Found;\n    protected override Expression VisitParameter(ParameterExpression p) { if (p.Name is null) Found = true; return p; }\n}","typeGuard":"static bool IsNamed(ParameterExpression p) => !string.IsNullOrEmpty(p.Name);","tryCatchPattern":null,"preventionTips":["Always pass a name: Expression.Parameter(type, \"name\").","Avoid closure capture of unnamed parameters.","Inline captured values as constants when possible."],"tags":["ef-core","linq","expression-tree","parameters","closures"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}