devlooped/moq · error · ArgumentException

Expression is not an event remove

Error message

Expression is not an event remove: {0}

What it means

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.

Solutions

  1. Use a remove-assignment expression: mock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>()).
  2. Use mock.SetupAdd if you meant the += accessor.
  3. Ensure the event is overridable/interface-based so Moq can intercept it.
  4. Check the expression body uses -= on an event member.

Example fix

// before
mock.SetupRemove(x => x.MyEvent += handler); // add, not remove
// after
mock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>());
Defensive patterns

Strategy: validation

Validate before calling

bool isRemoveExpr(Expression e) => e is BinaryExpression b && b.NodeType == ExpressionType.SubtractAssign && b.Left is MemberExpression me && me.Member is EventInfo;

Try / catch

try { mock.SetupRemove(x => x.MyEvent -= It.IsAny<EventHandler>()); }
catch (ArgumentException ex) when (ex.Message.StartsWith("Expression is not an event remove")) { /* fix lambda to use -= on an event */ throw; }

Prevention

When it happens

Trigger: Calling mock.SetupRemove with x => x.Event += handler (add instead of remove), a method call, or any non-remove-accessor expression body.

Common situations: Confusing SetupRemove with SetupAdd; verifying unsubscription logic but passing the subscription lambda by copy-paste; events that are not virtual/interface members.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/ac46f68d30d81196. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/Guard.cs:157

                    CultureInfo.CurrentCulture,
                    Resources.SetupNotEventAdd,
                    expression.ToStringFixed()),
                paramName);
        }

        public static void IsEventRemove(LambdaExpression expression, string paramName)
        {
            Debug.Assert(expression != null);

            switch (expression.Body.NodeType)
            {
                case ExpressionType.Call:
                    var call = (MethodCallExpression)expression.Body;
                    if (call.Method.IsEventRemoveAccessor()) return;
                    break;
            }

            throw new ArgumentException(
                string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.SetupNotEventRemove,
                    expression.ToStringFixed()),
                paramName);
        }

        /// <summary>
        /// Ensures the given <paramref name="value"/> is not null.
        /// Throws <see cref="ArgumentNullException"/> otherwise.
        /// </summary>
#if NULLABLE_REFERENCE_TYPES
        public static void NotNull([NotNull] object? value, string paramName)
#else
        public static void NotNull(object? value, string paramName)
#endif
        {
            if (value == null)

View on GitHub (pinned to 89a5be629c)