{"record":{"id":"7eaa93ef8d8dd409","repo":"devlooped/moq","slug":"expression-is-not-an-event-add-0","errorCode":null,"errorMessage":"Expression is not an event add: {0}","messagePattern":"Expression is not an event add: (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/Guard.cs","lineNumber":137,"sourceCode":"                    method.DeclaringType!.Name,\n                    method.Name,\n                    messageIfNotVisible));\n            }\n        }\n\n        public static void IsEventAdd(LambdaExpression expression, string paramName)\n        {\n            Debug.Assert(expression != null);\n\n            switch (expression.Body.NodeType)\n            {\n                case ExpressionType.Call:\n                    var call = (MethodCallExpression)expression.Body;\n                    if (call.Method.IsEventAddAccessor()) return;\n                    break;\n            }\n\n            throw new ArgumentException(\n                string.Format(\n                    CultureInfo.CurrentCulture,\n                    Resources.SetupNotEventAdd,\n                    expression.ToStringFixed()),\n                paramName);\n        }\n\n        public static void IsEventRemove(LambdaExpression expression, string paramName)\n        {\n            Debug.Assert(expression != null);\n\n            switch (expression.Body.NodeType)\n            {\n                case ExpressionType.Call:\n                    var call = (MethodCallExpression)expression.Body;\n                    if (call.Method.IsEventRemoveAccessor()) return;\n                    break;\n            }","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/Guard.cs#L119-L155","documentation":"This guard validates that the lambda passed to an event setup API (e.g. mock.SetupAdd) is an event add-accessor expression (x => x.MyEvent += handler). If the body is not an add assignment or a call to an add accessor, Moq throws ArgumentException. It ensures event-add setups receive only valid expressions.","triggerScenarios":"Calling mock.SetupAdd with the wrong expression form: a plain method call, a property, x => x.Event -= handler (remove instead of add), or a field-like event misuse.","commonSituations":"Swapping SetupAdd and SetupRemove accidentally; using SetupAdd for plain methods; writing the subscription lambda without the += operator; subscribing to events on non-virtual or non-mockable members.","solutions":["Use an add-assignment expression: mock.SetupAdd(x => x.MyEvent += It.IsAny<EventHandler>()).","Use mock.SetupRemove if you meant the -= accessor.","Ensure the event is virtual/overridable (or interface member) so it can be set up.","Verify the expression targets an event, not a method or property."],"exampleFix":"// before\nmock.SetupAdd(x => x.MyEvent -= handler); // remove, not add\n// after\nmock.SetupAdd(x => x.MyEvent += It.IsAny<EventHandler>());","handlingStrategy":"validation","validationCode":"bool isAddExpr(Expression e) => e is BinaryExpression b && b.NodeType == ExpressionType.AddAssign && b.Left is MemberExpression me && me.Member is EventInfo;","typeGuard":null,"tryCatchPattern":"try { mock.SetupAdd(x => x.MyEvent += It.IsAny<EventHandler>()); }\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"Expression is not an event add\")) { /* fix lambda to use += on an event */ throw; }","preventionTips":["SetupAdd requires `+=` on a virtual/interface event.","Use SetupRemove for `-=`.","Do not substitute method calls for event accessors in setup lambdas."],"tags":["moq","events","expression-tree"],"backgroundTag":"invalid-argument-value","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"}