{"record":{"id":"a274446ad20e568d","repo":"devlooped/moq","slug":"ex-message-from-replaceduck-expression-rewriting-rethrown","errorCode":null,"errorMessage":"ex.Message from ReplaceDuck expression rewriting (rethrown with paramName 'expression')","messagePattern":"ex\\.Message from ReplaceDuck expression rewriting \\(rethrown with paramName 'expression'\\)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/Protected/ProtectedAsMock.cs","lineNumber":42,"sourceCode":"        public ProtectedAsMock(Mock<T> mock)\n        {\n            Debug.Assert(mock != null);\n\n            this.mock = mock;\n        }\n\n        public ISetup<T> Setup(Expression<Action<TAnalog>> expression)\n        {\n            Guard.NotNull(expression, nameof(expression));\n\n            Expression<Action<T>> rewrittenExpression;\n            try\n            {\n                rewrittenExpression = (Expression<Action<T>>)ReplaceDuck(expression);\n            }\n            catch (ArgumentException ex)\n            {\n                throw new ArgumentException(ex.Message, nameof(expression));\n            }\n\n            var setup = Mock.Setup(this.mock, rewrittenExpression, null);\n            return new VoidSetupPhrase<T>(setup);\n        }\n\n        public ISetup<T, TResult> Setup<TResult>(Expression<Func<TAnalog, TResult>> expression)\n        {\n            Guard.NotNull(expression, nameof(expression));\n\n            Expression<Func<T, TResult>> rewrittenExpression;\n            try\n            {\n                rewrittenExpression = (Expression<Func<T, TResult>>)ReplaceDuck(expression);\n            }\n            catch (ArgumentException ex)\n            {\n                throw new ArgumentException(ex.Message, nameof(expression));","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/Protected/ProtectedAsMock.cs#L24-L60","documentation":"ProtectedAsMock.Setup rewrites the expression written against the 'analog' type TAnalog into an equivalent expression against the actual hidden type T via ReplaceDuck. When that rewriting encounters an invalid expression tree (e.g. a member that does not correspond to a protected member of T), it surfaces as ArgumentException with paramName 'expression', preserving the original message.","triggerScenarios":"Calling mock.Protected().As<TAnalog>().Setup(x => x.SomeMember(...)) where SomeMember cannot be mapped to a protected member of the mocked type, or the expression uses constructs ReplaceDuck cannot rewrite.","commonSituations":"Analog interface method signatures that drift from the actual protected members (renamed/changed visibility); typos in the analog interface; non-virtual or non-existent protected members.","solutions":["Ensure the TAnalog interface members exactly match the names, parameter types, and return types of the protected members on the mocked type","Verify the target protected member exists and is virtual/overridable on the mocked class","Read the inner message to identify which part of the expression failed the rewrite","Change the expression to only reference members declared on TAnalog"],"exampleFix":"// before (analog member mismatches protected member)\npublic interface IAnalog { int Calc(int x); }\nmock.Protected().As<IAnalog>().Setup(m => m.Calc(1));\n// after (match the actual protected signature)\npublic interface IAnalog { int Calculate(int x); }\nmock.Protected().As<IAnalog>().Setup(m => m.Calculate(1));","handlingStrategy":"try-catch","validationCode":"// Before setup, confirm the protected member exists on the mocked type:\nvar ok = typeof(MyService)\n    .GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)\n    .Any(m => m.Name == \"Calculate\" && m is MethodInfo mi && mi.IsVirtual);","typeGuard":null,"tryCatchPattern":"try\n{\n    mock.Protected().As<IAnalog>().Setup(m => m.Calculate(1));\n}\ncatch (ArgumentException ex) when (ex.ParamName == \"expression\")\n{\n    // Log ex.Message; fix the TAnalog member to match the protected member\n}","preventionTips":["Keep the analog interface in sync with protected members (rename refactors included)","Only declare members in TAnalog that mirror real protected members","Add a unit test that exercises every As<TAnalog>() setup so drift fails fast"],"tags":["moq","protected-members","expression-rewriting","argumentexception"],"backgroundTag":"invalid-argument","analyzedSha":"89a5be629c752960fe403e95ff583e4ae6f00542","analyzedAt":"2026-09-16T05:31:39.496Z","contentChangedAt":"2026-09-16T05:31:39.496Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}