{"record":{"id":"ac46f68d30d81196","repo":"devlooped/moq","slug":"expression-is-not-an-event-remove-0","errorCode":null,"errorMessage":"Expression is not an event remove: {0}","messagePattern":"Expression is not an event remove: (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Moq/Guard.cs","lineNumber":157,"sourceCode":"                    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            }\n\n            throw new ArgumentException(\n                string.Format(\n                    CultureInfo.CurrentCulture,\n                    Resources.SetupNotEventRemove,\n                    expression.ToStringFixed()),\n                paramName);\n        }\n\n        /// <summary>\n        /// Ensures the given <paramref name=\"value\"/> is not null.\n        /// Throws <see cref=\"ArgumentNullException\"/> otherwise.\n        /// </summary>\n#if NULLABLE_REFERENCE_TYPES\n        public static void NotNull([NotNull] object? value, string paramName)\n#else\n        public static void NotNull(object? value, string paramName)\n#endif\n        {\n            if (value == null)","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/devlooped/moq/blob/89a5be629c752960fe403e95ff583e4ae6f00542/src/Moq/Guard.cs#L139-L175","documentation":"The mirror of the event-add guard: it validates that the lambda passed to mock.SetupRemove is an event remove-accessor expression (x => x.MyEvent -= handler). Anything else (add assignment, method call, property access) causes Moq to throw ArgumentException with the offending expression.","triggerScenarios":"Calling mock.SetupRemove with x => x.Event += handler (add instead of remove), a method call, or any non-remove-accessor expression body.","commonSituations":"Confusing SetupRemove with SetupAdd; verifying unsubscription logic but passing the subscription lambda by copy-paste; events that are not virtual/interface members.","solutions":["Use a remove-assignment expression: mock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>()).","Use mock.SetupAdd if you meant the += accessor.","Ensure the event is overridable/interface-based so Moq can intercept it.","Check the expression body uses -= on an event member."],"exampleFix":"// before\nmock.SetupRemove(x => x.MyEvent += handler); // add, not remove\n// after\nmock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>());","handlingStrategy":"validation","validationCode":"bool isRemoveExpr(Expression e) => e is BinaryExpression b && b.NodeType == ExpressionType.SubtractAssign && b.Left is MemberExpression me && me.Member is EventInfo;","typeGuard":null,"tryCatchPattern":"try { mock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>()); }\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"Expression is not an event remove\")) { /* fix lambda to use -= on an event */ throw; }","preventionTips":["SetupRemove requires `-=` on a virtual/interface event.","Use SetupAdd for `+=`.","Copy-paste event setup lambdas carefully between add/remove."],"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"}