devlooped/moq · error · ArgumentException
Expression is not a property access
Error message
Expression is not a property access: {0} What it means
ToPropertyInfo extracts property metadata from an expression used in a property setup. It requires the lambda body to be a MemberExpression referring to a property access; anything else throws ArgumentException 'Expression is not a property access: {0}'. This keeps property setup APIs (mock.SetupProperty / Stub-style APIs) restricted to real properties.
Solutions
- Pass a direct property access on the lambda parameter: `x => x.PropertyName`.
- If you intended method setup, use `mock.Setup(x => x.Method())` instead of the property API.
- Ensure the member is a public instance property, not a field — convert the field to a property if needed.
- Verify the lambda body compiles to MemberExpression with NodeType MemberAccess; simplify multi-level chains to the property directly owned by the mock type.
Example fix
// before mock.SetupProperty(x => x.Compute()); // method, not property // after mock.SetupProperty(x => x.Value);
Defensive patterns
Strategy: type-guard
Validate before calling
static bool IsPropertyAccess<T, R>(Expression<Func<T, R>> e) =>
e.Body is MemberExpression { Member: PropertyInfo }; Type guard
static bool IsDirectProperty<T, R>(Expression<Func<T, R>> e) =>
e.Body is MemberExpression { Member: PropertyInfo p, Expression: ParameterExpression }
&& p.CanRead; Try / catch
try
{
mock.SetupProperty(expr);
}
catch (ArgumentException ex) when (ex.Message.StartsWith("Expression is not a property access"))
{
// switch to mock.Setup(...) for methods or fix the lambda
} Prevention
- Only pass single-level property lambdas like x => x.Prop to property APIs
- Fields are not properties — convert fields to properties before setup
- Use Setup(...) for method calls and computed expressions
- Validate the member type with reflection in helper wrappers
When it happens
Trigger: Passing a non-property lambda to an API that calls ToPropertyInfo: `x => x.Method()`, `x => x.Field + 1`, `x => x.Prop.Value` when Prop.Value resolves oddly, or an indexer access `x => x[0]`.
Common situations: Setting up fields instead of properties; accidentally passing method getters; writing `x => x.SomeProperty.SubProperty` where the target API expects a single-level property on the mock's type; dynamic/expanded expressions whose body is not MemberExpression.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- Unsupported expression
- Unsupported expression
- Expression involves a field access, which is not supported…
- It is impossible to call the provided strongly-typed…
- Can't set return value for void method
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/a2c6992cadfac57b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/ExpressionExtensions.cs:405
(derivedProperty.CanWrite(out var setter) && setter.GetBaseDefinition() == property.GetSetMethod(true)))
return derivedProperty;
}
}
return property;
}
/// <summary>
/// Converts the body of the lambda expression into the <see cref="PropertyInfo"/> referenced by it.
/// </summary>
public static PropertyInfo ToPropertyInfo(this LambdaExpression expression)
{
if (expression.Body is MemberExpression prop)
{
return prop.GetReboundProperty();
}
throw new ArgumentException(string.Format(
CultureInfo.CurrentCulture,
Resources.SetupNotProperty,
expression.ToStringFixed()));
}
/// <summary>
/// Checks whether the body of the lambda expression is a property access.
/// </summary>
public static bool IsProperty(this LambdaExpression expression)
{
Debug.Assert(expression != null);
return expression.Body is MemberExpression memberExpression && memberExpression.Member is PropertyInfo;
}
/// <summary>
/// Checks whether the body of the lambda expression is a indexer access.
/// </summary>View on GitHub (pinned to 89a5be629c)