{"record":{"id":"7dcd58cb35a883fb","repo":"microsoft/semantic-kernel","slug":"expression-must-be-a-property-access-expression","errorCode":null,"errorMessage":"Expression must be a property access expression","messagePattern":"Expression must be a property access expression","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs","lineNumber":206,"sourceCode":"            propertyPath.Insert(0, member.Name);\n\n            // If this is our first iteration, save the property type\n            if (propertyType == null)\n            {\n                propertyType = ((PropertyInfo)member).PropertyType;\n            }\n\n            // Move to the next level in the expression\n            expression = memberExpression.Expression;\n        }\n\n        if (expression is ParameterExpression)\n        {\n            // We've reached the parameter (e.g., 'myState'), which is good\n            return (propertyName, propertyPath.ToString(), propertyType ?? typeof(TProperty));\n        }\n\n        throw new ArgumentException(\"Expression must be a property access expression\", nameof(propertySelector));\n    }\n\n    #endregion\n\n    internal override KernelProcessStepInfo BuildStep(ProcessBuilder processBuilder, KernelProcessStepStateMetadata? stateMetadata = null)\n    {\n        KernelProcessMapStateMetadata? mapMetadata = stateMetadata as KernelProcessMapStateMetadata;\n\n        // Build the edges first\n        var builtEdges = this.Edges.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Select(e => e.Build()).ToList());\n        var agentActions = new ProcessAgentActions(\n            codeActions: new ProcessAgentCodeActions\n            {\n                OnComplete = this.OnCompleteCodeAction,\n                OnError = this.OnCompleteCodeAction\n            },\n            declarativeActions: new ProcessAgentDeclarativeActions\n            {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs#L188-L224","documentation":"Thrown by ProcessAgentBuilder.ExtractPropertyInfo when the lambda passed to WithUserStateInput is not a chain of property accesses ending at the lambda parameter (e.g., s => s.SomeProperty.NestedProperty). The method walks the expression tree via MemberExpression nodes; if the terminal node is not a ParameterExpression, the expression is rejected as invalid.","triggerScenarios":"Calling WithUserStateInput with a lambda that accesses a method call, a constant, a field (not a property), or a closure variable instead of a direct property chain on the state object. For example: s => s.ComputeValue() or s => _externalValue would both fail.","commonSituations":"Passing a computed expression or method invocation to WithUserStateInput instead of a simple property accessor. Using a field instead of a property in the state type. Capturing an external variable in the lambda closure rather than referencing the state parameter directly.","solutions":["Ensure the lambda is a pure property-access chain from the state parameter, e.g., s => s.MyProperty or s => s.Nested.Property.","Replace method calls and computed values with direct property accessors on the state type.","Convert any fields used in the state type to properties (with get/set)."],"exampleFix":"// before\nbuilder.WithUserStateInput(s => s.CalculateScore()); // method call — throws\n\n// after\nbuilder.WithUserStateInput(s => s.Score); // direct property access","handlingStrategy":"validation","validationCode":"// Validate the expression is a property-access chain before calling WithUserStateInput.\npublic static bool IsValidPropertyExpression<TState, TProperty>(Expression<Func<TState, TProperty>> expr)\n{\n    var body = expr.Body;\n    while (body is MemberExpression me)\n    {\n        body = me.Expression;\n    }\n    return body is ParameterExpression;\n}","typeGuard":"public static bool IsPropertyAccessExpression<TState, TProperty>(Expression<Func<TState, TProperty>> expr)\n{\n    var node = expr.Body;\n    while (node is MemberExpression me)\n    {\n        node = me.Expression;\n        if (me.Member is not PropertyInfo) return false;\n    }\n    return node is ParameterExpression;\n}","tryCatchPattern":null,"preventionTips":["Only pass simple property-access lambdas (s => s.Property) to WithUserStateInput.","Avoid method calls, fields, constants, and closure captures in the expression.","Ensure state type members are properties, not fields."],"tags":["semantic-kernel","process-framework","agent-builder","expression-trees","lambda","validation"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}