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

  1. Rewrite the setup expression so it is a simple constructor (`new`) expression or overridable member access that Moq supports.
  2. Make the mocked member virtual/abstract/interface-based so it can be intercepted without the unsupported expression shape.
  3. Split complex expressions into supported pieces or compute values outside the expression and pass them as locals/constants.
  4. 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

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


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)