devlooped/moq · error · Exception
Unhandled expression type
Error message
Unhandled expression type: {0} What it means
Moq's expression-to-string formatter (used to render lambda expressions like `x => x.Foo()` in verification messages) walks the expression tree with a switch over node types. When it encounters an expression node type it has no case for, it throws this generic Exception with the unhandled NodeType in the message. It is an internal completeness guard, meaning Moq's AppendExpression does not support that expression construct.
Solutions
- Identify the NodeType printed in the message and simplify the expression to constructs Moq supports (member access, method calls, simple binary/conditional expressions)
- Extract the unsupported construct into a plain helper method called from the expression where possible, or compute values outside the lambda and use a variable
- Verify with a different overload, e.g. capture the expression differently or use It.Is<T> predicates inside supported calls
- If the node type is a common one, file/patch an issue in Moq's StringBuilderExtensions.AppendExpression switch
Example fix
// before
mock.Verify(m => m.Foo(x => x switch { 1 => "a", _ => "b" }));
// after
string Pick(int x) => x switch { 1 => "a", _ => "b" };
mock.Verify(m => m.Foo(x => Pick(x))); Defensive patterns
Strategy: validation
Validate before calling
// keep setup/verify lambdas to member access, method calls and simple operators Expression<Func<T, TResult>> expr = x => x.Foo(1); // supported // avoid: switch expressions, ??=, dynamic, pattern constructs inside the lambda
Prevention
- Stick to simple expression bodies in Moq setup/verify lambdas
- Compute complex logic outside the expression and pass variables
- When upgrading C#, re-run tests covering Verify message rendering
- Watch the NodeType in the message to identify the unsupported construct
When it happens
Trigger: Passing a lambda to Mock.Of/Verify/Setup whose body uses an expression node type Moq cannot stringify — e.g. switch expressions, coalesce assignment, dynamic operations, index initializers in unusual positions, or newer C# expression forms added after the formatter was written.
Common situations: Using newer C# language features (C# 8/9+ syntax) inside setup/verify expressions; complex nested member-init or list-init expressions; upgrading the compiler/framework so previously unsupported nodes appear.
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
- Unhandled binding type
- Unsupported expression
- Expression is not a setter
- Expression is not an event add
- Expression is not an event remove
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/f44caed5cbd1ac05.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/StringBuilderExtensions.AppendExpression.cs:122
case ExpressionType.Invoke:
return builder.AppendExpression((InvocationExpression)expression);
case ExpressionType.MemberInit:
return builder.AppendExpression((MemberInitExpression)expression);
case ExpressionType.ListInit:
return builder.AppendExpression((ListInitExpression)expression);
case ExpressionType.Extension:
if (expression is MatchExpression me)
{
return builder.AppendExpression(me);
}
goto default;
default:
throw new Exception(string.Format(Resources.UnhandledExpressionType, expression.NodeType));
}
}
static StringBuilder AppendElementInit(this StringBuilder builder, ElementInit initializer)
{
return builder.AppendCommaSeparated("{ ", initializer.Arguments, AppendExpression, " }");
}
static StringBuilder AppendExpression(this StringBuilder builder, UnaryExpression expression)
{
switch (expression.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
return builder.Append('(')
.AppendNameOf(expression.Type)
.Append(')')
.AppendExpression(expression.Operand);View on GitHub (pinned to 89a5be629c)