devlooped/moq · error · NotSupportedException

NotSupportedException (no message: duck member is neither a…

Error message

NotSupportedException (no message: duck member is neither a method nor a property)

What it means

During duck-expression rewriting, DuckReplacer.FindCorrespondingMember only knows how to map MethodInfo and PropertyInfo members accessed on the analog parameter. If the lambda accesses any other member kind (e.g. a field, event, or nested type), a bare NotSupportedException with no message is thrown. This is a limitation guard of the analog-rewriting visitor.

Solutions

  1. Replace the field/event access in the analog with a property or method
  2. Restructure TAnalog as an interface exposing only methods and properties
  3. Access constant/field values outside the lambda (capture in a local) instead of through the analog parameter

Example fix

// before (analog class has a public field)
mock.Protected().As<Analog>().Verify(a => a.threshold);
// after
class Analog { public int Threshold { get; set; } }
mock.Protected().As<Analog>().Verify(a => a.Threshold);
Defensive patterns

Strategy: type-guard

Validate before calling

static bool IsLambdaMemberSupported(System.Linq.Expressions.MemberInfo m) => m is MethodInfo or PropertyInfo;

Type guard

static bool IsSupportedMember(System.Linq.Expressions.Expression e) =>
    e is System.Linq.Expressions.MemberExpression me && (me.Member is MethodInfo or PropertyInfo);

Try / catch

try { mock.Protected().As<Analog>().Verify(a => a.SomeMember); }
catch (NotSupportedException) { /* analog lambda used a field/event: restructure */ }

Prevention

When it happens

Trigger: mock.Protected().As<TAnalog>() lambdas that access a field (x => x.someField), an event, or any non-method/non-property member of TAnalog — reached via VisitMember during Setup/Verify/VerifyGet rewriting.

Common situations: Analog type is a class (not interface) exposing public fields; accessing constants/fields from the analog class; accidentally referencing a nested type in the lambda.

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/36838d503f2fbbef. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/Protected/ProtectedAsMock.cs:291

                else
                {
                    return base.VisitMember(node);
                }
            }

            MemberInfo FindCorrespondingMember(MemberInfo duckMember)
            {
                if (duckMember is MethodInfo duckMethod)
                {
                    return FindCorrespondingMethod(duckMethod);
                }
                else if (duckMember is PropertyInfo duckProperty)
                {
                    return FindCorrespondingProperty(duckProperty);
                }
                else
                {
                    throw new NotSupportedException();
                }
            }

            MethodInfo FindCorrespondingMethod(MethodInfo duckMethod)
            {
                var candidateTargetMethods =
                    this.targetType
                    .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
                    .Where(ctm => IsCorrespondingMethod(duckMethod, ctm))
                    .ToArray();

                if (candidateTargetMethods.Length == 0)
                {
                    throw new ArgumentException(string.Format(Resources.ProtectedMemberNotFound, this.targetType, duckMethod));
                }

                Debug.Assert(candidateTargetMethods.Length == 1);

View on GitHub (pinned to 89a5be629c)