devlooped/moq · error · NotSupportedException
Ref expression must evaluate to a constant value.
Error message
Ref expression must evaluate to a constant value.
What it means
Moq's MatcherFactory throws this NotSupportedException when a `Ref<T>` matcher argument's expression cannot be partially evaluated down to a constant value. `Ref` matches arguments by reference equality, so Moq must capture a constant value at setup time; if the expression is not constant (e.g. a variable or computed value), it cannot be used as a reference sentinel.
Solutions
- Use `new Ref<object>(value)` or pass a constant/readonly field value in the setup expression
- Assign the value to a `const` or capture it via a typed Ref sentinel so PartialEval yields a ConstantExpression
- If the value varies, use `It.Ref<object>.IsAny` instead of a fixed reference match
Example fix
// before object value = GetValue(); mock.Setup(m => m.DoWork(ref value)).Returns(true); // after mock.Setup(m => m.DoWork(ref It.Ref<object>.IsAny)).Returns(true);
Defensive patterns
Strategy: validation
Validate before calling
var captured = value; // ensure the ref arg is a constant/local capture
if (!(Expression.Constant(captured) is ConstantExpression)) throw new InvalidOperationException("Ref argument must be constant"); Type guard
static bool IsConstantRef(object? v) => v is string or int or long or double or bool or null;
Try / catch
try { mock.Setup(m => m.DoWork(ref arg))... } catch (NotSupportedException ex) when (ex.Message.Contains("Ref expression")) { /* use It.Ref<T>.IsAny */ } Prevention
- Use It.Ref<T>.IsAny for variable ref arguments
- Only pass constants or Ref<T> wrappers for ref matching
- Never pass mutable locals directly in ref setups
When it happens
Trigger: Calling It.Is or a setup with `Ref` semantics (via MatcherFactory.CreateMatcher) where the argument expression passed to the ref parameter is not a constant, e.g. `mock.Setup(m => m.Method(ref someLocalVar))` with a non-const local.
Common situations: Developers passing a mutable local variable or computed value to a ref parameter in a setup, expecting Moq to capture it; Moq 4.x requires a constant expression for ref matchers.
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
- Unsupported expression
- Resources.UnsupportedExpression (formatted with…
- Resources.UnsupportedExpression + expr.ToStringFixed() + "…
- Resources.UnsupportedExpression (formatted with expression)
- Unsupported expression
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/3124ee1a220f5041.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/MatcherFactory.cs:72
{
var memberDeclaringType = member.DeclaringType!;
if (memberDeclaringType.IsGenericType)
{
var memberDeclaringTypeDefinition = memberDeclaringType.GetGenericTypeDefinition();
if (memberDeclaringTypeDefinition == typeof(It.Ref<>))
{
return new Pair<IMatcher, Expression>(AnyMatcher.Instance, argument);
}
}
}
}
if (argument.PartialEval() is ConstantExpression constant)
{
return new Pair<IMatcher, Expression>(new RefMatcher(constant.Value), constant);
}
throw new NotSupportedException(Resources.RefExpressionMustBeConstantValue);
}
}
else if (parameter.IsDefined(typeof(ParamArrayAttribute), true) && argument.NodeType == ExpressionType.NewArrayInit)
{
var newArrayExpression = (NewArrayExpression)argument;
Debug.Assert(newArrayExpression.Type.IsArray);
var elementType = newArrayExpression.Type.GetElementType()!;
var n = newArrayExpression.Expressions.Count;
var matchers = new IMatcher[n];
var initializers = new Expression[n];
for (int i = 0; i < n; ++i)
{
(matchers[i], initializers[i]) = MatcherFactory.CreateMatcher(newArrayExpression.Expressions[i]);
initializers[i] = initializers[i].ConvertIfNeeded(elementType);
}View on GitHub (pinned to 89a5be629c)