{"record":{"id":"bcec64ec3ee51d07","repo":"devlooped/moq","slug":"resources-unsupportedexpression-expr-tostringfixed-in","errorCode":null,"errorMessage":"Resources.UnsupportedExpression + expr.ToStringFixed() + \" in \" + originalExpression + \":\\n\" + Resources.TypeNotMockable","messagePattern":"Resources\\.UnsupportedExpression \\+ expr\\.ToStringFixed\\(\\) \\+ \" in \" \\+ originalExpression \\+ \":\\\\n\" \\+ Resources\\.TypeNotMockable","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/Mock.cs","lineNumber":667,"sourceCode":"        static TSetup SetupRecursive<TSetup>(Mock mock, LambdaExpression originalExpression, Stack<MethodExpectation> parts, Func<Mock, Expression, MethodExpectation, TSetup> setupLast)\n            where TSetup : ISetup?\n        {\n            var part = parts.Pop();\n            var (expr, method, arguments) = part;\n\n            if (parts.Count == 0)\n            {\n                return setupLast(mock, originalExpression, part);\n            }\n            else\n            {\n                Mock? innerMock = mock.MutableSetups.FindLastInnerMock(setup => setup.Matches(part));\n                if (innerMock == null)\n                {\n                    var returnValue = mock.GetDefaultValue(method, out innerMock, useAlternateProvider: DefaultValueProvider.Mock);\n                    if (innerMock == null)\n                    {\n                        throw new ArgumentException(\n                            string.Format(\n                                CultureInfo.CurrentCulture,\n                                Resources.UnsupportedExpression,\n                                expr.ToStringFixed() + \" in \" + originalExpression.ToStringFixed() + \":\\n\" + Resources.TypeNotMockable));\n                    }\n                    var innerMockSetup = new InnerMockSetup(originalExpression, mock, expectation: part, returnValue);\n                    mock.MutableSetups.Add(innerMockSetup);\n                }\n                Debug.Assert(innerMock != null);\n\n                return Mock.SetupRecursive(innerMock, originalExpression, parts, setupLast);\n            }\n        }\n\n        internal static void SetupAllProperties(Mock mock)\n        {\n            mock.MutableSetups.Add(new StubbedPropertiesSetup(mock));\n        }","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/Mock.cs#L649-L685","documentation":"Moq throws this when a setup expression such as mock.Setup(x => x.SomeProperty.SomeMethod()) requires a nested (recursive) mock, but Moq cannot create one because the member's return type is not mockable (sealed/non-interface type without a accessible mock factory). It wraps the unsupported sub-expression and the full expression into an ArgumentException.","triggerScenarios":"mock.Setup(x => x.A.B.C) where an intermediate member's return type is sealed, static, a value type without mock support, or otherwise not mockable by Moq, so GetDefaultValue with DefaultValueProvider.Mock fails to yield an inner mock.","commonSituations":"Chained property navigation in setups against classes with sealed getters or non-mockable return types; trying to mock third-party sealed types; forgetting to make a nested property return an interface.","solutions":["Make the intermediate member's return type an interface or non-sealed class so Moq can mock it","Set up the full chain explicitly with separate stubs (e.g. return a manually created mock of the intermediate object) instead of relying on recursive mocks","Use Mock.Of<T>() / loose chains only over mockable types","Restructure the code under test so the deep chain is injected rather than navigated"],"exampleFix":"// before\nmock.Setup(x => x.Session.User.Name).Returns(\"bob\"); // Session returns sealed type -> throws\n// after\nvar sessionMock = new Mock<ISession>();\nsessionMock.Setup(s => s.User.Name).Returns(\"bob\");\nmock.Setup(x => x.Session).Returns(sessionMock.Object);","handlingStrategy":"type-guard","validationCode":"static bool IsMockableChain(Type t) => t.IsInterface || (!t.IsSealed && !t.IsValueType);\n// verify every intermediate member's return type in the setup chain is mockable before calling Setup","typeGuard":"static bool CanRecursiveMock(System.Linq.Expressions.Expression e) =>\n    e is System.Linq.Expressions.MemberExpression me &&\n    (me.Type.IsInterface || (!me.Type.IsSealed && !me.Type.IsValueType));","tryCatchPattern":"try\n{\n    mock.Setup(x => x.A.B.C).Returns(value);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"TypeNotMockable\"))\n{\n    // fall back to explicit stubbing of the chain\n}","preventionTips":["Design chained members to return interfaces","Avoid setups that navigate through sealed or struct types","Prefer injecting intermediate objects over deep navigation","Enable recursive mocking only over known-mockable return types"],"tags":["moq","unsupported-expression","recursive-mock","sealed-type"],"backgroundTag":"unsupported-operation","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"}