devlooped/moq · error · NotSupportedException
Unsupported expression
Error message
Unsupported expression: {0} What it means
Moq's expression visitor that extracts constructor-call arguments only supports Lambda, New, and Quote expression nodes. When a mock setup expression contains any other node type (e.g. a method call, member access, or constant used where a constructor call is expected), the visitor cannot interpret it and throws NotSupportedException. This is an internal guard indicating the setup expression does not match any supported pattern.
Solutions
- Rewrite the setup expression so it is a simple constructor (`new`) expression or overridable member access that Moq supports.
- Make the mocked member virtual/abstract/interface-based so it can be intercepted without the unsupported expression shape.
- Split complex expressions into supported pieces or compute values outside the expression and pass them as locals/constants.
- Inspect expression.ToStringFixed() in the message to identify the exact unsupported node and remove it.
Example fix
// before var mock = new Mock<Foo>(); mock.Setup(f => f.Bar(CreateBaz())); // method call inside setup expression // after var baz = CreateBaz(); mock.Setup(f => f.Bar(baz)); // value computed outside the expression
Defensive patterns
Strategy: try-catch
Validate before calling
bool isSupported = expr.Body is NewExpression || expr.Body is MemberExpression || expr.Body is MethodCallExpression;
Type guard
bool IsSupportedSetup(Expression e) => e is NewExpression || e is LambdaExpression l && (l.Body is NewExpression || l.Body is MemberExpression || l.Body is MethodCallExpression);
Try / catch
try { mock.Setup(f => f.Bar(CreateBaz())); }
catch (NotSupportedException ex) when (ex.Message.StartsWith("Unsupported expression")) { /* rewrite setup: precompute values outside the expression */ throw new InvalidOperationException("Fix setup expression", ex); } Prevention
- Keep setup lambdas simple: only member accesses on the mocked parameter.
- Compute argument values outside the expression and pass locals.
- Only mock virtual/abstract/interface members.
- Read expression.ToStringFixed() in the message to identify the offending node.
When it happens
Trigger: Calling Mock.Of, mock.Setup, or expression parsing APIs with a lambda whose body contains an unsupported node — e.g. setup on a property getter expression, factory-method invocation, or compound expression that routes through ConstructorCallVisitor's default branch instead of a `new` expression.
Common situations: Developers write setup expressions Moq cannot decompose: setting up a non-virtual or static member, using a factory method instead of `new`, or passing complex multi-statement expressions. Also seen when refactoring setups after a library version change tightened expression parsing.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Resources.UnsupportedExpression (formatted with…
- Expression is not a setter
- Expression is not an event add
- Expression is not an event remove
- Ref expression must evaluate to a constant value.
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/9777ebb84aeb7ac8.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/Expressions/Visitors/ConstructorCallVisitor.cs:57
ConstructorInfo? constructor;
object[] arguments;
#if NULLABLE_REFERENCE_TYPES
[return: NotNullIfNotNull("node")]
#endif
public override Expression? Visit(Expression? node)
{
switch (node?.NodeType)
{
case null:
return null;
case ExpressionType.Lambda:
case ExpressionType.New:
case ExpressionType.Quote:
return base.Visit(node);
default:
throw new NotSupportedException(
string.Format(
CultureInfo.CurrentCulture,
Resources.UnsupportedExpression,
node.ToStringFixed()));
}
}
protected override Expression VisitNew(NewExpression node)
{
constructor = node.Constructor;
// Creates a lambda which uses the same argument expressions as the
// arguments contained in the NewExpression
var argumentExtractor = Expression.Lambda<Func<object[]>>(
Expression.NewArrayInit(
typeof(object),
node.Arguments.Select(a => Expression.Convert(a, typeof(object)))));
arguments = ExpressionCompiler.Instance.Compile(argumentExtractor).Invoke();View on GitHub (pinned to 89a5be629c)