devlooped/moq · error · ArgumentException
ex.Message (re-thrown ArgumentException with paramName…
Error message
ex.Message (re-thrown ArgumentException with paramName 'expression')
What it means
Same rethrow pattern as Verify: ProtectedAsMock.VerifyGet rewrites the lambda from TAnalog to the mocked type T, and any ArgumentException during that rewriting (typically a missing matching protected property) is rethrown with paramName 'expression'. The message describes which protected member could not be matched.
Solutions
- Add or fix the corresponding protected property on the mocked type T so the duck-typing rewrite can match it
- Align TAnalog's property declaration with the actual protected property name/type on T
- Use Protected().SetupGet/VerifyGet with the exact string member name on the non-As API if the member is hard to express in the analog
- Use the strong-typed VerifyGet on Mock<T> if the property is public
Example fix
// before (analog property name does not exist as protected on T)
mock.Protected().As<IAnalog>().VerifyGet(a => a.Value);
// after
// class T { protected virtual string Value { get {...} } }
mock.Protected().As<IAnalog>().VerifyGet(a => a.Value); Defensive patterns
Strategy: validation
Validate before calling
static bool HasMatchingProtectedProperty(Type target, string name) =>
target.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Any(p => p.Name == name); Try / catch
try { mock.Protected().As<IAnalog>().VerifyGet(a => a.Value); }
catch (ArgumentException ex) when (ex.ParamName == "expression") { /* fix analog/target property mismatch */ } Prevention
- Keep TAnalog minimal (only members actually verified)
- Mirror exact property names and types from T
- Add a test that visits all TAnalog properties and checks existence on T
- Prefer SetupGet/VerifyGet with string names on Protected() when possible
When it happens
Trigger: Calling mock.Protected().As<TAnalog>().VerifyGet(x => x.SomeProperty, ...) where TAnalog's property has no corresponding protected property on T.
Common situations: Analog type declares a property that is a field or method on T, property renamed/made public on T, or the analog interface was hand-written against an older version of the mocked class.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ex.Message from ReplaceDuck expression rewriting (rethrown…
- Type does not have matching protected member
- Member . does not exist.
- No protected method . found whose signature is compatible…
- "public bool ( ) in class .
AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16).
Data as JSON: /api/errors/18d6b62da5561d62.
Report an issue: GitHub.
Appendix: source
Thrown at src/Moq/Protected/ProtectedAsMock.cs:209
{
Guard.NotNull(setterExpression, nameof(setterExpression));
var rewrittenExpression = ReconstructAndReplaceSetter(setterExpression);
Mock.VerifySet(mock, rewrittenExpression, times.HasValue ? times.Value : Times.AtLeastOnce(), failMessage);
}
public void VerifyGet<TProperty>(Expression<Func<TAnalog, TProperty>> expression, Times? times = null, string? failMessage = null)
{
Guard.NotNull(expression, nameof(expression));
Expression<Func<T, TProperty>> rewrittenExpression;
try
{
rewrittenExpression = (Expression<Func<T, TProperty>>)ReplaceDuck(expression);
}
catch (ArgumentException ex)
{
throw new ArgumentException(ex.Message, nameof(expression));
}
Mock.VerifyGet(this.mock, rewrittenExpression, times ?? Times.AtLeastOnce(), failMessage);
}
LambdaExpression ReconstructAndReplaceSetter(Action<TAnalog> setterExpression)
{
var expression = ExpressionReconstructor.Instance.ReconstructExpression(setterExpression, mock.ConstructorArguments);
return ReplaceDuck(expression);
}
static LambdaExpression ReplaceDuck(LambdaExpression expression)
{
Debug.Assert(expression.Parameters.Count == 1);
var targetParameter = Expression.Parameter(typeof(T), expression.Parameters[0].Name);
return Expression.Lambda(DuckReplacerInstance.Visit(expression.Body), targetParameter);
}View on GitHub (pinned to 89a5be629c)